Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
101 andreas 2
 * Copyright (C) 2020 to 2022 by Andreas Theofilu <andreas@theosys.at>
2 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
21 andreas 19
/** @file tqtmain.cpp
20
 * @brief Implements the surface of the application.
21
 *
22
 * This file implements the callback functions of the suface. While the most
23
 * classes are drawing the elements, the methods here take the ready elements
24
 * and display them. This file makes the surface completely independent of
25
 * the rest of the application which makes it easy to change the surface by
26
 * any other technology.
27
 */
2 andreas 28
#include <QApplication>
23 andreas 29
#include <QGuiApplication>
14 andreas 30
#include <QByteArray>
2 andreas 31
#include <QCommandLineParser>
32
#include <QCommandLineOption>
9 andreas 33
#include <QLabel>
2 andreas 34
#include <QtWidgets>
10 andreas 35
#include <QMouseEvent>
15 andreas 36
#include <QMoveEvent>
59 andreas 37
#include <QTouchEvent>
5 andreas 38
#include <QPalette>
9 andreas 39
#include <QPixmap>
7 andreas 40
#include <QFont>
41
#include <QFontDatabase>
21 andreas 42
#include <QtMultimediaWidgets/QVideoWidget>
43
#include <QtMultimedia/QMediaPlayer>
44
#include <QtMultimedia/QMediaPlaylist>
24 andreas 45
#include <QLayout>
40 andreas 46
#include <QSizePolicy>
21 andreas 47
#include <QUrl>
13 andreas 48
#include <QThread>
71 andreas 49
#include <QSound>
88 andreas 50
#ifdef __ANDROID__
51
#include <QAndroidJniObject>
52
#endif
5 andreas 53
#include <functional>
14 andreas 54
#include <mutex>
23 andreas 55
#ifdef __ANDROID__
56
#include <android/log.h>
57
#endif
4 andreas 58
#include "tpagemanager.h"
2 andreas 59
#include "tqtmain.h"
60
#include "tconfig.h"
61
#include "tqtsettings.h"
62 andreas 62
#include "tqkeyboard.h"
63
#include "tqkeypad.h"
5 andreas 64
#include "tcolor.h"
92 andreas 65
#include "texcept.h"
2 andreas 66
 
21 andreas 67
/**
44 andreas 68
 * @def THREAD_WAIT
69
 * This defines a time in milliseconds. It is used in the callbacks for the
70
 * functions to draw the elements. Qt need a little time to do it's work.
71
 * Therefore we're waiting a little bit. Otherwise it may result in missing
72
 * elements or black areas on the screen.
21 andreas 73
 */
100 andreas 74
#define THREAD_WAIT     1
44 andreas 75
 
76
extern amx::TAmxNet *gAmxNet;                   //!< Pointer to the class running the thread which handles the communication with the controller.
90 andreas 77
extern bool _restart_;                          //!< If this is set to true then the whole program will start over.
92 andreas 78
extern TPageManager *gPageManager;              //!< The pointer to the global defined main class.
21 andreas 79
std::mutex draw_mutex;                          //!< We're using threads and need to block execution sometimes
38 andreas 80
static bool isRunning = false;                  //!< TRUE = the pageManager was started.
107 andreas 81
TObject *gObject = nullptr;                     //!< Internal used pointer to a TObject class. (Necessary because of threads!)
100 andreas 82
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
44 andreas 83
static double gScale = 1.0;                     //!< Global variable holding the scale factor.
84
static int gFullWidth = 0;                      //!< Global variable holding the width of the AMX screen. This is used to calculate the scale factor for the settings dialog.
100 andreas 85
#endif
21 andreas 86
 
5 andreas 87
using std::bind;
61 andreas 88
using std::pair;
21 andreas 89
using std::string;
51 andreas 90
using std::vector;
5 andreas 91
 
107 andreas 92
string _NO_OBJECT = "The global class TObject is not available!";
93
 
21 andreas 94
/**
95
 * @brief qtmain is used here as the entry point for the surface.
96
 *
97
 * The main entry function parses the command line parameters, if there were
98
 * any and sets the basic attributes. It creates the main window and starts the
99
 * application.
100
 *
101
 * @param argc      The number of command line arguments
102
 * @param argv      A pointer to a 2 dimensional array containing the command
103
 *                  line parameters.
104
 * @param pmanager  A pointer to the page manager class which is the main class
105
 *                  managing everything.
106
 *
107
 * @return If no errors occured it returns 0.
108
 */
3 andreas 109
int qtmain(int argc, char **argv, TPageManager *pmanager)
2 andreas 110
{
31 andreas 111
    DECL_TRACER("qtmain(int argc, char **argv, TPageManager *pmanager)");
2 andreas 112
 
58 andreas 113
    if (!pmanager)
114
    {
115
        MSG_ERROR("Fatal: No pointer to the page manager received!");
116
        return 1;
117
    }
118
 
92 andreas 119
    if (!gPageManager || gPageManager != pmanager)
120
        gPageManager = pmanager;
88 andreas 121
#ifdef __ANDROID__
122
    MSG_INFO("Android API version: " << __ANDROID_API__);
3 andreas 123
 
88 andreas 124
#   if __ANDROID_API__ < 29
125
#       warn "The Android API version is less than 29! Some functions may not work!"
126
#   endif
127
#endif
58 andreas 128
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
38 andreas 129
    QApplication::setAttribute(Qt::AA_ForceRasterWidgets);
2 andreas 130
#endif
131
 
5 andreas 132
    QApplication app(argc, argv);
58 andreas 133
    // Set the orientation
134
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
135
    QScreen *screen = QGuiApplication::primaryScreen();
136
 
137
    if (!screen)
138
    {
139
        MSG_ERROR("Couldn't determine the primary screen!")
140
        return 1;
141
    }
142
 
143
    if (pmanager->getSettings()->getRotate() == 1)  // portrait?
88 andreas 144
    {
145
        MSG_INFO("Orientation set to portrait mode.");
58 andreas 146
        screen->setOrientationUpdateMask(Qt::PortraitOrientation);
88 andreas 147
    }
148
    else
149
    {
150
        MSG_INFO("Orientation set to landscape mode.");
151
        screen->setOrientationUpdateMask(Qt::LandscapeOrientation);
152
    }
58 andreas 153
 
38 andreas 154
    double scale = 1.0;
24 andreas 155
    // Calculate the scale factor
156
    if (TConfig::getScale())
157
    {
43 andreas 158
        // Because we've no window here we can not know on which screen, if
159
        // there are more than one, the application will start. Because on a
160
        // mobile mostly no external screen is connected, we take always the
161
        // resolution of the first (built in) screen.
162
        // TODO: Find a way to get the screen the application will start and
163
        // take this screen to calculate the scale factor.
24 andreas 164
        QList<QScreen *> screens = QGuiApplication::screens();
165
        QRect screenGeometry = screens.at(0)->availableGeometry();
88 andreas 166
        double width = 0.0;
167
        double height = 0.0;
24 andreas 168
        int minWidth = pmanager->getSettings()->getWith();
169
        int minHeight = pmanager->getSettings()->getHeight();
88 andreas 170
 
171
        if (pmanager->getSettings()->getRotate() == 1)  // portrait?
172
        {
173
            width = std::min(screenGeometry.width(), screenGeometry.height());
174
            height = std::max(screenGeometry.height(), screenGeometry.width());
175
        }
176
        else
177
        {
178
            width = std::max(screenGeometry.width(), screenGeometry.height());
179
            height = std::min(screenGeometry.height(), screenGeometry.width());
180
        }
181
 
31 andreas 182
        MSG_INFO("Dimension of AMX screen:" << minWidth << " x " << minHeight);
88 andreas 183
        MSG_INFO("Screen size: " << width << " x " << height);
43 andreas 184
        // The scale factor is always calculated in difference to the prefered
185
        // size of the original AMX panel.
24 andreas 186
        double scaleW = width / minWidth;
187
        double scaleH = height / minHeight;
38 andreas 188
        scale = std::min(scaleW, scaleH);
43 andreas 189
        gScale = scale;     // The calculated scale factor
88 andreas 190
        gFullWidth = width;
43 andreas 191
        // This preprocessor variable allows the scaling to be done by the Skia
192
        // library, which is used to draw everything. In comparison to Qt this
193
        // library is a bit slower and sometimes does not honor the aspect ratio
44 andreas 194
        // correct. But in case there is another framework than Qt in use, this
43 andreas 195
        // could be necessary.
196
#ifdef _SCALE_SKIA_
26 andreas 197
        if (scale != 0.0)
198
        {
199
            pmanager->setScaleFactor(scale);
31 andreas 200
            MSG_INFO("Scale factor: " << scale);
26 andreas 201
        }
31 andreas 202
 
203
        if (scaleW != 0.0)
204
            pmanager->setScaleFactorWidth(scaleW);
205
 
206
        if (scaleH != 0.0)
207
            pmanager->setScaleFactorHeight(scaleH);
58 andreas 208
#endif  // _SCALE_SKIA_
24 andreas 209
    }
58 andreas 210
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
23 andreas 211
 
24 andreas 212
    // Initialize the application
5 andreas 213
    QCoreApplication::setOrganizationName(TConfig::getProgName().c_str());
214
    QCoreApplication::setApplicationName("AMX panel simulator");
24 andreas 215
    QCoreApplication::setApplicationVersion(VERSION_STRING());
5 andreas 216
    QCommandLineParser parser;
217
    parser.setApplicationDescription(QCoreApplication::applicationName());
218
    parser.addHelpOption();
219
    parser.addVersionOption();
220
    parser.process(app);
2 andreas 221
 
5 andreas 222
    MainWindow mainWin;
58 andreas 223
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
224
#   ifndef _SCALE_SKIA_
43 andreas 225
    if (TConfig::getScale() && scale != 1.0)
38 andreas 226
        mainWin.setScaleFactor(scale);
58 andreas 227
#   endif   // _SCALE_SKIA_
228
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
22 andreas 229
    mainWin.setConfigFile(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
43 andreas 230
    mainWin.setStyleSheet("QMainWindow {background: 'black';}");    // Keep the background black. Helps to save battery on OLED displays.
58 andreas 231
    mainWin.grabGesture(Qt::PinchGesture);
38 andreas 232
 
5 andreas 233
    mainWin.show();
234
    return app.exec();
2 andreas 235
}
236
 
21 andreas 237
/**
238
 * @brief MainWindow::MainWindow constructor
239
 *
240
 * This method is the constructor for this class. It registers the callback
241
 * functions to the class TPageManager and starts the main run loop.
43 andreas 242
 *
243
 * Qt is used only to manage widgets to handle pages and subpages. A page as
244
 * well as a subpage may contain a background graphic and some elements. The
245
 * elements could be buttons, bargraphs and other objects. The underlying layer
246
 * draw every element as a ready graphic image and call a callback function to
247
 * let Qt display the graphic.
248
 * If there are some animations on a subpage defined, this is also handled by
249
 * Qt. Especialy sliding and fading.
21 andreas 250
 */
2 andreas 251
MainWindow::MainWindow()
252
{
5 andreas 253
    DECL_TRACER("MainWindow::MainWindow()");
59 andreas 254
 
107 andreas 255
    gObject = new TObject;
256
 
59 andreas 257
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
258
    setAttribute(Qt::WA_AcceptTouchEvents, true);   // We accept touch events
259
    grabGesture(Qt::PinchGesture);                  // We use a pinch gesture to open the settings dialog
88 andreas 260
 
92 andreas 261
    if (gPageManager && gPageManager->getSettings()->getRotate() == 1)  // portrait?
88 andreas 262
    {
263
        MSG_INFO("Orientation set to portrait mode.");
264
        _setOrientation(O_PORTRAIT);
265
    }
266
    else
267
    {
268
        MSG_INFO("Orientation set to landscape mode.");
269
        _setOrientation(O_LANDSCAPE);
270
    }
107 andreas 271
#else
113 andreas 272
    setWindowIcon(QIcon(":images/icon.png"));
59 andreas 273
#endif
92 andreas 274
    if (!gPageManager)
275
    {
276
        EXCEPTFATALMSG("The class TPageManager was not initialized!");
277
    }
278
 
43 andreas 279
    // First we register all our surface callbacks to the underlying work
280
    // layer. All the graphics are drawn by the Skia library. The layer below
281
    // call the following functions to let Qt display the graphics on the
282
    // screen let it manage the widgets containing the graphics.
92 andreas 283
    gPageManager->registerCallbackDB(bind(&MainWindow::_displayButton, this,
5 andreas 284
                                       std::placeholders::_1,
285
                                       std::placeholders::_2,
286
                                       std::placeholders::_3,
287
                                       std::placeholders::_4,
288
                                       std::placeholders::_5,
289
                                       std::placeholders::_6,
290
                                       std::placeholders::_7,
291
                                       std::placeholders::_8));
292
 
92 andreas 293
    gPageManager->registerCallbackSP(bind(&MainWindow::_setPage, this,
5 andreas 294
                                         std::placeholders::_1,
295
                                         std::placeholders::_2,
296
                                         std::placeholders::_3));
297
 
92 andreas 298
    gPageManager->registerCallbackSSP(bind(&MainWindow::_setSubPage, this,
5 andreas 299
                                          std::placeholders::_1,
300
                                          std::placeholders::_2,
301
                                          std::placeholders::_3,
302
                                          std::placeholders::_4,
41 andreas 303
                                          std::placeholders::_5,
304
                                          std::placeholders::_6));
5 andreas 305
 
92 andreas 306
    gPageManager->registerCallbackSB(bind(&MainWindow::_setBackground, this,
5 andreas 307
                                         std::placeholders::_1,
308
                                         std::placeholders::_2,
309
                                         std::placeholders::_3,
310
                                         std::placeholders::_4,
38 andreas 311
                                         std::placeholders::_5,
312
                                         std::placeholders::_6,
313
                                         std::placeholders::_7));
5 andreas 314
 
92 andreas 315
    gPageManager->regCallDropPage(bind(&MainWindow::_dropPage, this, std::placeholders::_1));
316
    gPageManager->regCallDropSubPage(bind(&MainWindow::_dropSubPage, this, std::placeholders::_1));
317
    gPageManager->regCallPlayVideo(bind(&MainWindow::_playVideo, this,
21 andreas 318
                                       std::placeholders::_1,
319
                                       std::placeholders::_2,
320
                                       std::placeholders::_3,
321
                                       std::placeholders::_4,
322
                                       std::placeholders::_5,
323
                                       std::placeholders::_6,
324
                                       std::placeholders::_7,
325
                                       std::placeholders::_8,
326
                                       std::placeholders::_9));
92 andreas 327
    gPageManager->regCallInputText(bind(&MainWindow::_inputText, this, std::placeholders::_1, std::placeholders::_2));
328
    gPageManager->regCallbackKeyboard(bind(&MainWindow::_showKeyboard, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
329
    gPageManager->regCallbackKeypad(bind(&MainWindow::_showKeypad, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
330
    gPageManager->regCallResetKeyboard(bind(&MainWindow::_resetKeyboard, this));
331
    gPageManager->regCallShowSetup(bind(&MainWindow::_showSetup, this));
332
    gPageManager->regCallbackResetSurface(bind(&MainWindow::_resetSurface, this));
333
    gPageManager->regCallbackShutdown(bind(&MainWindow::_shutdown, this));
334
    gPageManager->regCallbackPlaySound(bind(&MainWindow::_playSound, this, std::placeholders::_1));
98 andreas 335
    gPageManager->registerCBsetVisible(bind(&MainWindow::_setVisible, this, std::placeholders::_1, std::placeholders::_2));
111 andreas 336
    gPageManager->regSendVirtualKeys(bind(&MainWindow::_sendVirtualKeys, this, std::placeholders::_1));
92 andreas 337
    gPageManager->deployCallbacks();
5 andreas 338
    createActions();
339
 
2 andreas 340
#ifndef QT_NO_SESSIONMANAGER
5 andreas 341
    QGuiApplication::setFallbackSessionManagementEnabled(false);
342
    connect(qApp, &QGuiApplication::commitDataRequest,
343
            this, &MainWindow::writeSettings);
2 andreas 344
#endif
43 andreas 345
    // Some types used to transport data from the layer below.
14 andreas 346
    qRegisterMetaType<size_t>("size_t");
347
    qRegisterMetaType<QByteArray>("QByteArray");
41 andreas 348
    qRegisterMetaType<ANIMATION_t>("ANIMATION_t");
50 andreas 349
    qRegisterMetaType<Button::TButton *>("TButton");
62 andreas 350
    qRegisterMetaType<std::string>("std::string");
14 andreas 351
 
43 andreas 352
    // All the callback functions doesn't act directly. Instead they emit an
353
    // event. Then Qt decides whether the real function is started directly and
354
    // immediately or if the call is queued and called later in a thread. To
355
    // handle this we're "connecting" the real functions to some signals.
15 andreas 356
    try
357
    {
358
        connect(this, &MainWindow::sigDisplayButton, this, &MainWindow::displayButton);
359
        connect(this, &MainWindow::sigSetPage, this, &MainWindow::setPage);
360
        connect(this, &MainWindow::sigSetSubPage, this, &MainWindow::setSubPage);
361
        connect(this, &MainWindow::sigSetBackground, this, &MainWindow::setBackground);
362
        connect(this, &MainWindow::sigDropPage, this, &MainWindow::dropPage);
363
        connect(this, &MainWindow::sigDropSubPage, this, &MainWindow::dropSubPage);
21 andreas 364
        connect(this, &MainWindow::sigPlayVideo, this, &MainWindow::playVideo);
50 andreas 365
        connect(this, &MainWindow::sigInputText, this, &MainWindow::inputText);
62 andreas 366
        connect(this, &MainWindow::sigKeyboard, this, &MainWindow::showKeyboard);
367
        connect(this, &MainWindow::sigKeypad, this, &MainWindow::showKeypad);
64 andreas 368
        connect(this, &MainWindow::sigShowSetup, this, &MainWindow::showSetup);
71 andreas 369
        connect(this, &MainWindow::sigPlaySound, this, &MainWindow::playSound);
98 andreas 370
        connect(this, &MainWindow::sigDropButton, this, &MainWindow::dropButton);
371
        connect(this, &MainWindow::sigSetVisible, this, &MainWindow::SetVisible);
111 andreas 372
        connect(this, &MainWindow::sigSendVirtualKeys, this, &MainWindow::sendVirtualKeys);
31 andreas 373
        connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::appStateChanged);
15 andreas 374
    }
375
    catch (std::exception& e)
376
    {
377
        MSG_ERROR("Connection error: " << e.what());
378
    }
93 andreas 379
    catch(...)
380
    {
381
        MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
382
    }
15 andreas 383
 
5 andreas 384
    setUnifiedTitleAndToolBarOnMac(true);
61 andreas 385
#ifdef __ANDROID__
386
    // At least initialize the phone call listener
387
    if (gPageManager)
388
        gPageManager->initPhoneState();
92 andreas 389
 
93 andreas 390
    /*
391
     * In case this class was created not the first time, we initiate a small
392
     * thread to send the signal ApplicationActive to initiate the communication
393
     * with the controller again. This also starts the page manager thread
394
     * (TPageManager), which handles all elements of the surface.
395
     */
92 andreas 396
    if (_restart_ && gPageManager)
397
    {
93 andreas 398
        try
399
        {
400
            std::thread mThread = std::thread([=] { this->_signalState(Qt::ApplicationActive); });
401
            mThread.detach();   // We detach immediately to leave this method.
402
        }
403
        catch (std::exception& e)
404
        {
405
            MSG_ERROR("Error starting the thread to reinvoke communication!");
406
        }
407
        catch(...)
408
        {
409
            MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
410
        }
92 andreas 411
    }
93 andreas 412
#endif
92 andreas 413
    _restart_ = false;
2 andreas 414
}
415
 
34 andreas 416
MainWindow::~MainWindow()
417
{
418
    DECL_TRACER("MainWindow::~MainWindow()");
40 andreas 419
 
34 andreas 420
    killed = true;
421
    prg_stopped = true;
422
 
423
    if (gAmxNet && !gAmxNet->isStopped())
424
        gAmxNet->stop();
90 andreas 425
 
426
    if (mToolbar)
427
    {
428
        removeToolBar(mToolbar);
429
        mToolbar = nullptr;
430
    }
431
 
107 andreas 432
    if (gObject)
433
    {
434
        delete gObject;
435
        gObject = nullptr;
436
    }
437
 
90 andreas 438
    isRunning = false;
34 andreas 439
}
440
 
93 andreas 441
#ifdef __ANDROID__
21 andreas 442
/**
93 andreas 443
 * @brief Small thread to invoke the initialization on an Android device.
444
 *
445
 * On Android devices the signal ApplicationActive is not send if the class
446
 * \p MainWindow is destroyed and recreated. Therefore we need this little
447
 * helper to send the signal when the class is initialized.
448
 *
449
 * @param state     This defines the signal to send.
450
 */
451
void MainWindow::_signalState(Qt::ApplicationState state)
452
{
453
    DECL_TRACER("MainWindow::_signalState(Qt::ApplicationState state)");
454
 
455
    std::this_thread::sleep_for(std::chrono::seconds(1));   // Wait a second
456
    appStateChanged(state);
457
}
458
#endif
459
 
460
/**
21 andreas 461
 * @brief MainWindow::closeEvent called when the application receives an exit event.
462
 *
463
 * If the user clicks on the exit icon or on the menu point _Exit_ this method
464
 * is called. It makes sure everything is written to the configuration file
465
 * and accepts the evernt.
466
 *
467
 * @param event The exit event
468
 */
2 andreas 469
void MainWindow::closeEvent(QCloseEvent *event)
470
{
5 andreas 471
    DECL_TRACER("MainWindow::closeEvent(QCloseEvent *event)");
24 andreas 472
#ifdef __ANDROID__
23 andreas 473
    __android_log_print(ANDROID_LOG_DEBUG,"tpanel","Close event; settingsChanged=%s", (settingsChanged ? "true":"false"));
24 andreas 474
#endif
5 andreas 475
    if (settingsChanged)
476
    {
477
        writeSettings();
478
        event->accept();
479
    }
480
    else
481
    {
21 andreas 482
        prg_stopped = true;
34 andreas 483
        killed = true;
36 andreas 484
        MSG_INFO("Program will stop!");
37 andreas 485
#ifdef __ANDROID__
92 andreas 486
        if (gPageManager)
487
            gPageManager->stopNetworkState();
37 andreas 488
#endif
5 andreas 489
        event->accept();
490
    }
2 andreas 491
}
492
 
21 andreas 493
/**
494
 * @brief MainWindow::eventFilter filters the QEvent::MouseMove event
61 andreas 495
 * Beside the filtering of the MouseEvent, it waits for a gesture and
496
 * call the method gestureEvent() which handles the gesture and opens the
497
 * setting dialog.
21 andreas 498
 * @param obj   The object where the event occured.
499
 * @param event The event.
500
 * @return `true` when the event should be ignored.
501
 */
15 andreas 502
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
503
{
504
    if (event->type() == QEvent::MouseMove)
505
        return true;    // Filter event out, i.e. stop it being handled further.
506
 
59 andreas 507
    if (event->type() == QEvent::Gesture)
508
        return gestureEvent(static_cast<QGestureEvent*>(event));
509
 
15 andreas 510
    return QMainWindow::eventFilter(obj, event);
511
}
512
 
21 andreas 513
/**
57 andreas 514
 * @brief MainWindow::event Looks for a gesture
515
 * @param event The event occured
516
 * @return TRUE if event was accepted
517
 */
518
bool MainWindow::event(QEvent* event)
519
{
520
    if (event->type() == QEvent::Gesture)
521
        return gestureEvent(static_cast<QGestureEvent*>(event));
522
 
523
    return QWidget::event(event);
524
}
525
 
526
/**
527
 * @brief MainWindow::gestureEvent handles a pinch event
528
 * If a pinch event occured where the scale factor increased, the settings
529
 * dialog is called. This exists for devices, where the left toolbox is not
530
 * visible.
531
 * @param event The guesture occured
532
 * @return TRUE
533
 */
534
bool MainWindow::gestureEvent(QGestureEvent* event)
535
{
536
    DECL_TRACER("MainWindow::gestureEvent(QGestureEvent* event)");
59 andreas 537
 
57 andreas 538
    if (QGesture *pinch = event->gesture(Qt::PinchGesture))
539
    {
540
        string gs;
541
 
542
        QPinchGesture *pg = static_cast<QPinchGesture *>(pinch);
543
 
544
        switch(pg->state())
545
        {
546
            case Qt::NoGesture:         gs.assign("no gesture"); break;
547
            case Qt::GestureStarted:    gs.assign("gesture started"); break;
548
            case Qt::GestureUpdated:    gs.assign("gesture updated"); break;
549
            case Qt::GestureFinished:   gs.assign("gesture finished"); break;
550
            case Qt::GestureCanceled:   gs.assign("gesture canceled"); break;
551
        }
552
 
59 andreas 553
        MSG_DEBUG("PinchGesture state " << gs << " detected");
57 andreas 554
 
555
        if (pg->state() == Qt::GestureFinished)
556
        {
59 andreas 557
            MSG_DEBUG("total scale: " << pg->totalScaleFactor() << ", scale: " << pg->scaleFactor() << ", last scale: " << pg->lastScaleFactor());
558
 
57 andreas 559
            if (pg->totalScaleFactor() > pg->scaleFactor())
560
                settings();
561
        }
562
    }
563
 
564
    return false;
565
}
566
 
567
/**
39 andreas 568
 * @brief MainWindow::createActions creates the toolbar on the right side.
21 andreas 569
 */
2 andreas 570
void MainWindow::createActions()
571
{
5 andreas 572
    DECL_TRACER("MainWindow::createActions()");
2 andreas 573
 
88 andreas 574
    // Add a mToolbar (on the right side)
575
    mToolbar = new QToolBar(this);
58 andreas 576
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
88 andreas 577
    mToolbar->setAllowedAreas(Qt::RightToolBarArea);
578
    mToolbar->setFloatable(false);
579
    mToolbar->setMovable(false);
31 andreas 580
 
92 andreas 581
    if (TConfig::getScale() && gPageManager && gScale != 1.0)
38 andreas 582
    {
92 andreas 583
        int width = (int)((double)gPageManager->getSettings()->getWith() * gScale);
38 andreas 584
        int tbWidth = (int)(48.0 * gScale);
585
        int icWidth = (int)(40.0 * gScale);
586
 
587
        if ((gFullWidth - width) < tbWidth)
588
        {
88 andreas 589
            delete mToolbar;
590
            mToolbar = nullptr;
38 andreas 591
            return;
592
        }
593
 
594
        QSize iSize(icWidth, icWidth);
88 andreas 595
        mToolbar->setIconSize(iSize);
38 andreas 596
    }
39 andreas 597
#else
88 andreas 598
    mToolbar->setFloatable(true);
599
    mToolbar->setMovable(true);
600
    mToolbar->setAllowedAreas(Qt::RightToolBarArea | Qt::BottomToolBarArea);
39 andreas 601
#endif
32 andreas 602
    QAction *arrowUpAct = new QAction(QIcon(":/images/arrow_up.png"), tr("Up"), this);
603
    connect(arrowUpAct, &QAction::triggered, this, &MainWindow::arrowUp);
88 andreas 604
    mToolbar->addAction(arrowUpAct);
31 andreas 605
 
32 andreas 606
    QAction *arrowLeftAct = new QAction(QIcon(":/images/arrow_left.png"), tr("Left"), this);
607
    connect(arrowLeftAct, &QAction::triggered, this, &MainWindow::arrowLeft);
88 andreas 608
    mToolbar->addAction(arrowLeftAct);
31 andreas 609
 
32 andreas 610
    QAction *arrowRightAct = new QAction(QIcon(":/images/arrow_right.png"), tr("Right"), this);
611
    connect(arrowRightAct, &QAction::triggered, this, &MainWindow::arrowRight);
88 andreas 612
    mToolbar->addAction(arrowRightAct);
31 andreas 613
 
32 andreas 614
    QAction *arrowDownAct = new QAction(QIcon(":/images/arrow_down.png"), tr("Down"), this);
615
    connect(arrowDownAct, &QAction::triggered, this, &MainWindow::arrowDown);
88 andreas 616
    mToolbar->addAction(arrowDownAct);
31 andreas 617
 
32 andreas 618
    QAction *selectOkAct = new QAction(QIcon(":/images/ok.png"), tr("Ok"), this);
619
    connect(selectOkAct, &QAction::triggered, this, &MainWindow::selectOk);
88 andreas 620
    mToolbar->addAction(selectOkAct);
31 andreas 621
 
88 andreas 622
    mToolbar->addSeparator();
31 andreas 623
 
35 andreas 624
    QToolButton *btVolUp = new QToolButton(this);
625
    btVolUp->setIcon(QIcon(":/images/vol_up.png"));
626
    connect(btVolUp, &QToolButton::pressed, this, &MainWindow::volumeUpPressed);
627
    connect(btVolUp, &QToolButton::released, this, &MainWindow::volumeUpReleased);
88 andreas 628
    mToolbar->addWidget(btVolUp);
40 andreas 629
 
35 andreas 630
    QToolButton *btVolDown = new QToolButton(this);
631
    btVolDown->setIcon(QIcon(":/images/vol_down.png"));
632
    connect(btVolDown, &QToolButton::pressed, this, &MainWindow::volumeDownPressed);
633
    connect(btVolDown, &QToolButton::released, this, &MainWindow::volumeDownReleased);
88 andreas 634
    mToolbar->addWidget(btVolDown);
38 andreas 635
/*
33 andreas 636
    QAction *volMute = new QAction(QIcon(":/images/vol_mute.png"), tr("X"), this);
637
    connect(volMute, &QAction::triggered, this, &MainWindow::volumeMute);
88 andreas 638
    mToolbar->addAction(volMute);
38 andreas 639
*/
88 andreas 640
    mToolbar->addSeparator();
40 andreas 641
 
36 andreas 642
    const QIcon settingsIcon = QIcon::fromTheme("settings-configure", QIcon(":/images/settings.png"));
5 andreas 643
    QAction *settingsAct = new QAction(settingsIcon, tr("&Settings..."), this);
644
    settingsAct->setStatusTip(tr("Change the settings"));
645
    connect(settingsAct, &QAction::triggered, this, &MainWindow::settings);
88 andreas 646
    mToolbar->addAction(settingsAct);
2 andreas 647
 
38 andreas 648
    const QIcon aboutIcon = QIcon::fromTheme("help-about", QIcon(":/images/info.png"));
5 andreas 649
    QAction *aboutAct = new QAction(aboutIcon, tr("&About..."), this);
650
    aboutAct->setShortcuts(QKeySequence::Open);
651
    aboutAct->setStatusTip(tr("About this program"));
652
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
88 andreas 653
    mToolbar->addAction(aboutAct);
2 andreas 654
 
33 andreas 655
    const QIcon exitIcon = QIcon::fromTheme("application-exit", QIcon(":/images/off.png"));
88 andreas 656
    QAction *exitAct = mToolbar->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
5 andreas 657
    exitAct->setShortcuts(QKeySequence::Quit);
658
    exitAct->setStatusTip(tr("Exit the application"));
31 andreas 659
 
88 andreas 660
    addToolBar(Qt::RightToolBarArea, mToolbar);
2 andreas 661
}
662
 
21 andreas 663
/**
664
 * @brief MainWindow::mousePressEvent catches the event Qt::LeftButton.
665
 *
666
 * If the user presses the left mouse button somewhere in the main window, this
667
 * method is triggered. It retrieves the position of the mouse pointer and
668
 * sends it to the page manager TPageManager.
669
 *
670
 * @param event The event
671
 */
10 andreas 672
void MainWindow::mousePressEvent(QMouseEvent* event)
673
{
70 andreas 674
    DECL_TRACER("MainWindow::mousePressEvent(QMouseEvent* event)");
675
 
10 andreas 676
    if(event->button() == Qt::LeftButton)
38 andreas 677
    {
678
        int x = event->x();
679
        int y = event->y();
680
 
70 andreas 681
        mLastPressX = x;
682
        mLastPressY = y;
683
 
38 andreas 684
        if (isScaled())
685
        {
686
            x = (int)((double)x / mScaleFactor);
687
            y = (int)((double)y / mScaleFactor);
688
        }
689
 
92 andreas 690
        gPageManager->mouseEvent(x, y, true);
38 andreas 691
    }
40 andreas 692
    else if (event->button() == Qt::MiddleButton)
693
        settings();
10 andreas 694
}
695
 
21 andreas 696
/**
697
 * @brief MainWindow::mouseReleaseEvent catches the event Qt::LeftButton.
698
 *
699
 * If the user releases the left mouse button somewhere in the main window, this
700
 * method is triggered. It retrieves the position of the mouse pointer and
701
 * sends it to the page manager TPageManager.
702
 *
703
 * @param event The event
704
 */
10 andreas 705
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
706
{
70 andreas 707
    DECL_TRACER("MainWindow::mouseReleaseEvent(QMouseEvent* event)");
708
 
10 andreas 709
    if(event->button() == Qt::LeftButton)
38 andreas 710
    {
101 andreas 711
        int x = ((mLastPressX >= 0) ? mLastPressX : event->x());
712
        int y = ((mLastPressY >= 0) ? mLastPressY : event->y());
38 andreas 713
 
70 andreas 714
        mLastPressX = mLastPressY = -1;
715
 
38 andreas 716
        if (isScaled())
717
        {
718
            x = (int)((double)x / mScaleFactor);
719
            y = (int)((double)y / mScaleFactor);
720
        }
721
 
92 andreas 722
        gPageManager->mouseEvent(x, y, false);
38 andreas 723
    }
10 andreas 724
}
725
 
21 andreas 726
/**
727
 * @brief MainWindow::settings initiates the configuration dialog.
728
 */
2 andreas 729
void MainWindow::settings()
730
{
5 andreas 731
    DECL_TRACER("MainWindow::settings()");
2 andreas 732
 
90 andreas 733
    // Save some old values to decide whether to start over or not.
734
    string oldHost = TConfig::getController();
735
    int oldPort = TConfig::getPort();
736
    int oldChannelID = TConfig::getChannel();
737
    // Initialize and open the settings dialog.
5 andreas 738
    TQtSettings *dlg_settings = new TQtSettings(this);
58 andreas 739
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
43 andreas 740
    // On mobile devices we set the scale factor always because otherwise the
741
    // dialog will be unusable.
742
    dlg_settings->setScaleFactor(gScale);
40 andreas 743
    dlg_settings->doResize();
744
#endif
23 andreas 745
    int ret = dlg_settings->exec();
746
 
747
    if (ret && dlg_settings->hasChanged())
89 andreas 748
    {
23 andreas 749
        writeSettings();
89 andreas 750
 
90 andreas 751
        if (TConfig::getController() != oldHost ||
752
            TConfig::getChannel() != oldChannelID ||
753
            TConfig::getPort() != oldPort)
89 andreas 754
        {
90 andreas 755
            // Start over by exiting this class
756
            MSG_INFO("Program will start over!");
757
            _restart_ = true;
758
            prg_stopped = true;
759
            killed = true;
760
 
761
            if (gAmxNet)
762
                gAmxNet->stop();
763
 
764
            close();
89 andreas 765
        }
766
    }
23 andreas 767
    else if (!ret && dlg_settings->hasChanged())
768
    {
769
        TConfig cf(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
770
    }
44 andreas 771
 
772
    delete dlg_settings;
2 andreas 773
}
774
 
21 andreas 775
/**
776
 * @brief MainWindow::writeSettings Writes the settings into the configuration file.
777
 */
2 andreas 778
void MainWindow::writeSettings()
779
{
5 andreas 780
    DECL_TRACER("MainWindow::writeSettings()");
23 andreas 781
 
782
    TConfig::saveSettings();
43 andreas 783
    MSG_INFO("Wrote settings.");
2 andreas 784
}
785
 
21 andreas 786
/**
787
 * @brief MainWindow::about displays the _about_ dialog.
788
 */
2 andreas 789
void MainWindow::about()
790
{
5 andreas 791
    DECL_TRACER("MainWindow::about()");
2 andreas 792
 
82 andreas 793
    std::string msg = "Simulation of an AMX G4 panel\n";
794
    msg.append("Version v").append(VERSION_STRING()).append("\n");
88 andreas 795
    msg.append("(C) Copyright 2020 to 2022 by Andreas Theofilu <andreas@theosys.at>\n");
82 andreas 796
    msg.append("This program is under the terms of GPL version 3");
797
 
5 andreas 798
    QMessageBox::about(this, tr(std::string("About ").append(TConfig::getProgName()).c_str()),
82 andreas 799
                       tr(msg.c_str()));
2 andreas 800
}
801
 
32 andreas 802
void MainWindow::arrowUp()
803
{
804
    DECL_TRACER("MainWindow::arrowUp()");
35 andreas 805
 
806
    extButtons_t btType = EXT_CURSOR_UP;
807
 
808
    if (TConfig::getPanelType().find("Android") != string::npos)
809
        btType = EXT_GESTURE_UP;
810
 
92 andreas 811
    gPageManager->externalButton(btType, true);
812
    gPageManager->externalButton(btType, false);
32 andreas 813
}
14 andreas 814
 
32 andreas 815
void MainWindow::arrowLeft()
816
{
817
    DECL_TRACER("MainWindow::arrowLeft()");
35 andreas 818
    extButtons_t btType = EXT_CURSOR_LEFT;
819
 
820
    if (TConfig::getPanelType().find("Android") != string::npos)
821
        btType = EXT_GESTURE_LEFT;
822
 
92 andreas 823
    gPageManager->externalButton(btType, true);
824
    gPageManager->externalButton(btType, false);
32 andreas 825
}
826
 
827
void MainWindow::arrowRight()
828
{
829
    DECL_TRACER("MainWindow::arrowRight()");
35 andreas 830
    extButtons_t btType = EXT_CURSOR_RIGHT;
831
 
832
    if (TConfig::getPanelType().find("Android") != string::npos)
833
        btType = EXT_GESTURE_RIGHT;
834
 
92 andreas 835
    gPageManager->externalButton(btType, true);
836
    gPageManager->externalButton(btType, false);
32 andreas 837
}
838
 
839
void MainWindow::arrowDown()
840
{
841
    DECL_TRACER("MainWindow::arrowDown()");
35 andreas 842
    extButtons_t btType = EXT_CURSOR_DOWN;
843
 
844
    if (TConfig::getPanelType().find("Android") != string::npos)
845
        btType = EXT_GESTURE_DOWN;
846
 
92 andreas 847
    gPageManager->externalButton(btType, true);
848
    gPageManager->externalButton(btType, false);
32 andreas 849
}
850
 
851
void MainWindow::selectOk()
852
{
853
    DECL_TRACER("MainWindow::selectOk()");
35 andreas 854
    extButtons_t btType = EXT_CURSOR_SELECT;
855
 
856
    if (TConfig::getPanelType().find("Android") != string::npos)
857
        btType = EXT_GESTURE_DOUBLE_PRESS;
858
 
92 andreas 859
    gPageManager->externalButton(btType, true);
860
    gPageManager->externalButton(btType, false);
32 andreas 861
}
862
 
33 andreas 863
void MainWindow::volumeUpPressed()
864
{
865
    DECL_TRACER("MainWindow::volumeUpPressed()");
35 andreas 866
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
867
 
868
    if (TConfig::getPanelType().find("Android") != string::npos)
869
        btType = EXT_GESTURE_ROTATE_RIGHT;
870
 
92 andreas 871
    gPageManager->externalButton(btType, true);
33 andreas 872
}
873
 
874
void MainWindow::volumeUpReleased()
875
{
876
    DECL_TRACER("MainWindow::volumeUpReleased()");
35 andreas 877
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
878
 
879
    if (TConfig::getPanelType().find("Android") != string::npos)
880
        btType = EXT_GESTURE_ROTATE_RIGHT;
881
 
92 andreas 882
    gPageManager->externalButton(btType, false);
33 andreas 883
}
884
 
885
void MainWindow::volumeDownPressed()
886
{
887
    DECL_TRACER("MainWindow::volumeDownPressed()");
35 andreas 888
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
889
 
890
    if (TConfig::getPanelType().find("Android") != string::npos)
891
        btType = EXT_GESTURE_ROTATE_LEFT;
892
 
92 andreas 893
    gPageManager->externalButton(btType, true);
33 andreas 894
}
895
 
896
void MainWindow::volumeDownReleased()
897
{
898
    DECL_TRACER("MainWindow::volumeDownReleased()");
35 andreas 899
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
900
 
901
    if (TConfig::getPanelType().find("Android") != string::npos)
902
        btType = EXT_GESTURE_ROTATE_LEFT;
903
 
92 andreas 904
    gPageManager->externalButton(btType, false);
33 andreas 905
}
38 andreas 906
/*
33 andreas 907
void MainWindow::volumeMute()
908
{
909
    DECL_TRACER("MainWindow::volumeMute()");
92 andreas 910
    gPageManager->externalButton(EXT_GENERAL, true);
911
    gPageManager->externalButton(EXT_GENERAL, false);
33 andreas 912
}
38 andreas 913
*/
42 andreas 914
void MainWindow::animationFinished()
915
{
916
    DECL_TRACER("MainWindow::animationFinished()");
43 andreas 917
 
107 andreas 918
    if (!gObject)
919
    {
920
        MSG_ERROR(_NO_OBJECT);
921
        return;
922
    }
43 andreas 923
 
107 andreas 924
    TObject::OBJECT_t *obj = gObject->getMarkedRemove();
925
 
42 andreas 926
    while (obj)
927
    {
928
        if (obj->animation && obj->animation->state() != QAbstractAnimation::Running)
929
            break;
43 andreas 930
 
107 andreas 931
        obj = gObject->getNextMarkedRemove(obj);
42 andreas 932
    }
43 andreas 933
 
101 andreas 934
    if (obj && obj->animation)
42 andreas 935
    {
107 andreas 936
        MSG_DEBUG("Dropping object " << TObject::handleToString(obj->handle));
42 andreas 937
        delete obj->animation;
938
        obj->animation = nullptr;
107 andreas 939
        gObject->dropContent(obj);
101 andreas 940
 
941
        if (mLastObject == obj)
942
            mLastObject = nullptr;
943
 
107 andreas 944
        gObject->removeObject(obj->handle);
42 andreas 945
    }
946
    else
947
    {
948
        MSG_WARNING("No or invalid object to delete!");
949
    }
950
}
951
 
51 andreas 952
void MainWindow::textChangedMultiLine()
953
{
954
    DECL_TRACER("MainWindow::textChangedMultiLine(const QString& text)");
955
    // Find out from which input line the signal was send
956
    QTextEdit* edit = qobject_cast<QTextEdit*>(sender());
957
 
958
    if (!edit)
959
    {
960
        MSG_ERROR("No QTextEdit widget found! External sender?");
961
        return;
962
    }
963
 
107 andreas 964
    if (!gObject)
965
    {
966
        MSG_ERROR(_NO_OBJECT);
967
        return;
968
    }
969
 
51 andreas 970
    QString text = edit->toPlainText();
971
    WId id = edit->winId();
107 andreas 972
    TObject::OBJECT_t *obj = gObject->findObject(id);
51 andreas 973
 
974
    if (!obj)
975
    {
976
        MSG_ERROR("No object witb WId " << id << " found!");
977
        return;
978
    }
979
 
92 andreas 980
    if (gPageManager)
981
        gPageManager->setTextToButton(obj->handle, text.toStdString());
51 andreas 982
}
983
 
984
void MainWindow::textSingleLineReturn()
985
{
986
    DECL_TRACER("MainWindow::textChangedSingleLine(const QString& text)");
987
 
988
    // Find out from which input line the signal was send
989
    QLineEdit* edit = qobject_cast<QLineEdit*>(sender());
990
 
991
    if (!edit)
992
    {
993
        MSG_ERROR("No QLineEdit widget found! External sender?");
994
        return;
995
    }
996
 
107 andreas 997
    if (!gObject)
998
    {
999
        MSG_ERROR(_NO_OBJECT);
1000
        return;
1001
    }
1002
 
51 andreas 1003
    QString text = edit->text();
1004
    WId id = edit->winId();
107 andreas 1005
    TObject::OBJECT_t *obj = gObject->findObject(id);
51 andreas 1006
 
1007
    if (!obj)
1008
    {
1009
        MSG_ERROR("No object with WId " << id << " found!");
1010
        return;
1011
    }
1012
 
1013
    MSG_DEBUG("Writing text: " << text.toStdString());
1014
 
92 andreas 1015
    if (gPageManager)
1016
        gPageManager->setTextToButton(obj->handle, text.toStdString());
51 andreas 1017
}
1018
 
61 andreas 1019
/**
1020
 * @brief MainWindow::appStateChanged - Is called whenever the state of the app changes.
1021
 * This callback method is called whenever the state of the application
1022
 * changes. This is mostly usefull on mobile devices. Whenever the main window
1023
 * looses the focus (screen closed, application is put into background, ...)
1024
 * this method is called and updates a flag. If the application is not able
1025
 * to draw to the screen (suspended) all events are cached. At the moment the
1026
 * application becomes active, all queued messages are applied.
1027
 * @param state     The new state of the application.
1028
 */
31 andreas 1029
void MainWindow::appStateChanged(Qt::ApplicationState state)
1030
{
32 andreas 1031
    DECL_TRACER("MainWindow::appStateChanged(Qt::ApplicationState state)");
31 andreas 1032
 
1033
    switch (state)
1034
    {
61 andreas 1035
        case Qt::ApplicationSuspended:              // Should not occure on a normal desktop
36 andreas 1036
            MSG_INFO("Switched to mode SUSPEND");
31 andreas 1037
            mHasFocus = false;
1038
        break;
61 andreas 1039
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)      // On a normal desktop we can ignore this signals
31 andreas 1040
        case Qt::ApplicationInactive:
36 andreas 1041
            MSG_INFO("Switched to mode INACTIVE");
31 andreas 1042
            mHasFocus = false;
1043
        break;
1044
 
1045
        case Qt::ApplicationHidden:
36 andreas 1046
            MSG_INFO("Switched to mode HIDDEN");
31 andreas 1047
            mHasFocus = false;
1048
        break;
61 andreas 1049
#endif
31 andreas 1050
        case Qt::ApplicationActive:
36 andreas 1051
            MSG_INFO("Switched to mode ACTIVE");
31 andreas 1052
            mHasFocus = true;
38 andreas 1053
 
92 andreas 1054
            if (!isRunning && gPageManager)
38 andreas 1055
            {
1056
                // Start the core application
92 andreas 1057
                gPageManager->startUp();
1058
                gPageManager->run();
38 andreas 1059
                isRunning = true;
1060
            }
1061
            else
1062
                playShowList();
31 andreas 1063
        break;
61 andreas 1064
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
1065
        default:
1066
            mHasFocus = true;
1067
#endif
31 andreas 1068
    }
61 andreas 1069
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
92 andreas 1070
    if (mHasFocus && gPageManager)
38 andreas 1071
    {
92 andreas 1072
        gPageManager->initNetworkState();
1073
        gPageManager->initBatteryState();
38 andreas 1074
    }
92 andreas 1075
    else if (gPageManager)
38 andreas 1076
    {
92 andreas 1077
        gPageManager->stopNetworkState();
1078
        gPageManager->stopBatteryState();
38 andreas 1079
    }
37 andreas 1080
#endif
31 andreas 1081
}
1082
 
64 andreas 1083
void MainWindow::_shutdown()
1084
{
1085
    DECL_TRACER("MainWindow::_shutdown()")
1086
 
1087
    close();
1088
}
1089
 
32 andreas 1090
/******************* Signal handling *************************/
1091
 
44 andreas 1092
void MainWindow::_resetSurface()
1093
{
1094
    DECL_TRACER("MainWindow::_resetSurface()");
1095
 
90 andreas 1096
    // Start over by exiting this class
1097
    MSG_INFO("Program will start over!");
1098
    _restart_ = true;
1099
    prg_stopped = true;
1100
    killed = true;
88 andreas 1101
 
90 andreas 1102
    if (gAmxNet)
1103
        gAmxNet->stop();
88 andreas 1104
 
90 andreas 1105
    close();
44 andreas 1106
}
1107
 
14 andreas 1108
void MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)
4 andreas 1109
{
14 andreas 1110
    DECL_TRACER("MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)");
1111
 
21 andreas 1112
    if (prg_stopped)
1113
        return;
1114
 
61 andreas 1115
    if (!mHasFocus)     // Suspended?
1116
    {
1117
        addButton(handle, parent, buffer, pixline, left, top, width, height);
1118
        return;
1119
    }
1120
 
14 andreas 1121
    QByteArray buf;
1122
 
1123
    if (buffer && pixline > 0)
1124
    {
1125
        size_t size = width * height * (pixline / width);
15 andreas 1126
        MSG_DEBUG("Buffer size=" << size << ", width=" << width << ", height=" << height << ", left=" << left << ", top=" << top);
14 andreas 1127
        buf.insert(0, (const char *)buffer, size);
1128
    }
1129
 
15 andreas 1130
    try
1131
    {
1132
        emit sigDisplayButton(handle, parent, buf, width, height, pixline, left, top);
1133
    }
1134
    catch (std::exception& e)
1135
    {
1136
        MSG_ERROR("Error triggering function \"displayButton()\": " << e.what());
1137
    }
98 andreas 1138
 
44 andreas 1139
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
14 andreas 1140
}
1141
 
98 andreas 1142
void MainWindow::_setVisible(ulong handle, bool state)
1143
{
1144
    DECL_TRACER("MainWindow::_setVisible(ulong handle, bool state)");
1145
 
1146
    if (prg_stopped)
1147
        return;
1148
 
107 andreas 1149
    MSG_DEBUG("Object " << TObject::handleToString(handle) << " marked for " << (state ? "VISIBLE" : "INVISIBLE") << " state.");
98 andreas 1150
    emit sigSetVisible(handle, state);
1151
}
1152
 
14 andreas 1153
void MainWindow::_setPage(ulong handle, int width, int height)
1154
{
1155
    DECL_TRACER("MainWindow::_setPage(ulong handle, int width, int height)");
1156
 
21 andreas 1157
    if (prg_stopped)
1158
        return;
1159
 
61 andreas 1160
    if (!mHasFocus)
1161
    {
1162
        addPage(handle, width, height);
1163
        return;
1164
    }
1165
 
14 andreas 1166
    emit sigSetPage(handle, width, height);
57 andreas 1167
#ifndef __ANDROID__
44 andreas 1168
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1169
#endif
14 andreas 1170
}
1171
 
41 andreas 1172
void MainWindow::_setSubPage(ulong handle, int left, int top, int width, int height, ANIMATION_t animate)
14 andreas 1173
{
1174
    DECL_TRACER("MainWindow::_setSubPage(ulong handle, int left, int top, int width, int height)");
1175
 
21 andreas 1176
    if (prg_stopped)
1177
        return;
1178
 
61 andreas 1179
    if (!mHasFocus)
1180
    {
1181
        addSubPage(handle, left, top, width, height, animate);
1182
        return;
1183
    }
1184
 
41 andreas 1185
    emit sigSetSubPage(handle, left, top, width, height, animate);
57 andreas 1186
#ifndef __ANDROID__
44 andreas 1187
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1188
#endif
14 andreas 1189
}
1190
 
38 andreas 1191
void MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, int width, int height, ulong color)
14 andreas 1192
{
1193
    DECL_TRACER("MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, ulong color)");
1194
 
21 andreas 1195
    if (prg_stopped)
1196
        return;
1197
 
61 andreas 1198
    if (!mHasFocus)
1199
    {
1200
        addBackground(handle, image, size, rowBytes, width, height, color);
1201
        return;
1202
    }
1203
 
14 andreas 1204
    QByteArray buf;
1205
 
1206
    if (image && size > 0)
1207
        buf.insert(0, (const char *)image, size);
1208
 
38 andreas 1209
    emit sigSetBackground(handle, buf, rowBytes, width, height, color);
57 andreas 1210
#ifndef __ANDROID__
44 andreas 1211
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1212
#endif
14 andreas 1213
}
1214
 
1215
void MainWindow::_dropPage(ulong handle)
1216
{
1217
    DECL_TRACER("MainWindow::_dropPage(ulong handle)");
1218
 
70 andreas 1219
    doReleaseButton();
1220
 
61 andreas 1221
    if (!mHasFocus)
1222
    {
1223
        markDrop(handle);
1224
        return;
1225
    }
1226
 
14 andreas 1227
    emit sigDropPage(handle);
70 andreas 1228
 
57 andreas 1229
#ifndef __ANDROID__
44 andreas 1230
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1231
#endif
14 andreas 1232
}
1233
 
1234
void MainWindow::_dropSubPage(ulong handle)
1235
{
1236
    DECL_TRACER("MainWindow::_dropSubPage(ulong handle)");
1237
 
70 andreas 1238
    doReleaseButton();
1239
 
61 andreas 1240
    if (!mHasFocus)
1241
    {
1242
        markDrop(handle);
1243
        return;
1244
    }
1245
 
14 andreas 1246
    emit sigDropSubPage(handle);
70 andreas 1247
 
57 andreas 1248
#ifndef __ANDROID__
44 andreas 1249
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1250
#endif
14 andreas 1251
}
1252
 
98 andreas 1253
void MainWindow::_dropButton(ulong handle)
1254
{
1255
    DECL_TRACER("MainWindow::_dropButton(ulong handle)");
1256
 
1257
    if (!mHasFocus)
1258
    {
1259
        markDrop(handle);
1260
        return;
1261
    }
1262
 
1263
    emit sigDropButton(handle);
1264
}
1265
 
21 andreas 1266
void MainWindow::_playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
1267
{
1268
    DECL_TRACER("MainWindow::_playVideo(ulong handle, const string& url)");
1269
 
1270
    if (prg_stopped)
1271
        return;
1272
 
61 andreas 1273
    if (!mHasFocus)
1274
    {
1275
        addVideo(handle, parent, left, top, width, height, url, user, pw);
1276
        return;
1277
    }
1278
 
21 andreas 1279
    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
57 andreas 1280
#ifndef __ANDROID__
44 andreas 1281
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1282
#endif
21 andreas 1283
}
1284
 
52 andreas 1285
void MainWindow::_inputText(Button::TButton* button, Button::BITMAP_t& bm)
50 andreas 1286
{
52 andreas 1287
    DECL_TRACER("MainWindow::_inputText(Button::TButton* button, Button::BITMAP_t& bm)");
50 andreas 1288
 
1289
    if (prg_stopped)
1290
        return;
1291
 
61 andreas 1292
    if (!mHasFocus)
1293
    {
1294
        addInText(button->getHandle(), button, bm);
1295
        return;
1296
    }
1297
 
52 andreas 1298
    QByteArray buf;
1299
 
1300
    if (bm.buffer && bm.rowBytes > 0)
1301
    {
1302
        size_t size = bm.width * bm.height * (bm.rowBytes / bm.width);
1303
        buf.insert(0, (const char *)bm.buffer, size);
1304
    }
57 andreas 1305
 
52 andreas 1306
    emit sigInputText(button, buf, bm.width, bm.height, bm.rowBytes);
57 andreas 1307
#ifndef __ANDROID__
50 andreas 1308
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 1309
#endif
50 andreas 1310
}
1311
 
63 andreas 1312
void MainWindow::_showKeyboard(const std::string& init, const std::string& prompt, bool priv)
62 andreas 1313
{
63 andreas 1314
    DECL_TRACER("MainWindow::_showKeyboard(std::string &init, std::string &prompt, bool priv)");
62 andreas 1315
 
1316
    if (prg_stopped)
1317
        return;
1318
 
70 andreas 1319
    doReleaseButton();
63 andreas 1320
    emit sigKeyboard(init, prompt, priv);
62 andreas 1321
}
1322
 
63 andreas 1323
void MainWindow::_showKeypad(const std::string& init, const std::string& prompt, bool priv)
62 andreas 1324
{
63 andreas 1325
    DECL_TRACER("MainWindow::_showKeypad(std::string &init, std::string &prompt, bool priv)");
62 andreas 1326
 
1327
    if (prg_stopped)
1328
        return;
1329
 
70 andreas 1330
    doReleaseButton();
63 andreas 1331
    emit sigKeypad(init, prompt, priv);
62 andreas 1332
}
1333
 
63 andreas 1334
void MainWindow::_resetKeyboard()
1335
{
1336
    DECL_TRACER("MainWindow::_resetKeyboard()");
1337
 
1338
    emit sigResetKeyboard();
1339
}
1340
 
64 andreas 1341
void MainWindow::_showSetup()
1342
{
1343
    DECL_TRACER("MainWindow::_showSetup()");
1344
 
1345
    emit sigShowSetup();
1346
}
1347
 
71 andreas 1348
void MainWindow::_playSound(const string& file)
1349
{
1350
    DECL_TRACER("MainWindow::_playSound(const string& file)");
1351
 
1352
    emit sigPlaySound(file);
1353
}
1354
 
88 andreas 1355
void MainWindow::_setOrientation(J_ORIENTATION ori)
1356
{
1357
#ifdef __ANDROID__
1358
    DECL_TRACER("MainWindow::_setOriantation(J_ORIENTATION ori)");
1359
 
1360
    QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
1361
 
1362
    if ( activity.isValid() )
1363
    {
1364
        activity.callMethod<void>
1365
                ("setRequestedOrientation"  // method name
1366
                 , "(I)V"                   // signature
1367
                 , ori);
1368
    }
1369
#endif
1370
}
1371
 
111 andreas 1372
void MainWindow::_sendVirtualKeys(const string& str)
1373
{
1374
    DECL_TRACER("MainWindow::_sendVirtualKeys(const string& str)");
1375
 
1376
    emit sigSendVirtualKeys(str);
1377
}
1378
 
70 andreas 1379
void MainWindow::doReleaseButton()
1380
{
1381
    DECL_TRACER("MainWindow::doReleaseButton()");
1382
 
92 andreas 1383
    if (mLastPressX >= 0 && mLastPressX >= 0 && gPageManager)
70 andreas 1384
    {
1385
        MSG_DEBUG("Sending outstanding mouse release event for coordinates x" << mLastPressX << ", y" << mLastPressY);
1386
        int x = mLastPressX;
1387
        int y = mLastPressY;
1388
 
1389
        if (isScaled())
1390
        {
1391
            x = (int)((double)x / mScaleFactor);
1392
            y = (int)((double)y / mScaleFactor);
1393
        }
1394
 
92 andreas 1395
        gPageManager->mouseEvent(x, y, false);
70 andreas 1396
    }
1397
 
1398
    mLastPressX = mLastPressY = -1;
1399
}
1400
 
14 andreas 1401
/******************* Draw elements *************************/
1402
 
1403
void MainWindow::displayButton(ulong handle, ulong parent, QByteArray buffer, int width, int height, int pixline, int left, int top)
1404
{
5 andreas 1405
    DECL_TRACER("MainWindow::displayButton(ulong handle, unsigned char* buffer, size_t size, int width, int height, int pixline, int left, int top)");
4 andreas 1406
 
107 andreas 1407
    if (!gObject)
1408
    {
1409
        MSG_ERROR(_NO_OBJECT);
1410
        return;
1411
    }
1412
 
1413
    draw_mutex.lock();
1414
 
38 andreas 1415
    if (isScaled())
1416
    {
1417
        MSG_DEBUG("Scaling to factor " << mScaleFactor);
1418
    }
1419
 
107 andreas 1420
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1421
    TObject::OBJECT_t *par = gObject->findObject(parent);
1422
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << TObject::handleToString(parent));
5 andreas 1423
 
6 andreas 1424
    if (!par)
1425
    {
59 andreas 1426
        if (TStreamError::checkFilter(HLOG_DEBUG))
107 andreas 1427
            MSG_WARNING("Button " << TObject::handleToString(handle) << " has no parent (" << TObject::handleToString(parent) << ")! Ignoring it.");
59 andreas 1428
 
14 andreas 1429
        draw_mutex.unlock();
6 andreas 1430
        return;
1431
    }
1432
 
107 andreas 1433
    if (par->animation && !par->aniDirection)
1434
    {
1435
        if (par->animation->state() == QAbstractAnimation::Running)
1436
        {
1437
            MSG_WARNING("Object " << TObject::handleToString(parent) << " is busy with an animation!");
1438
            par->animation->stop();
1439
        }
1440
        else
1441
        {
1442
            MSG_WARNING("Object " << TObject::handleToString(parent) << " has not finished the animation!");
1443
        }
1444
 
1445
        draw_mutex.unlock();
1446
        return;
1447
    }
1448
    else if (par->remove)
1449
    {
1450
        MSG_WARNING("Object " << TObject::handleToString(parent) << " is marked for remove. Will not draw image!");
1451
        draw_mutex.unlock();
1452
        return;
1453
    }
1454
 
5 andreas 1455
    if (!obj)
1456
    {
107 andreas 1457
        MSG_DEBUG("Adding new object " << TObject::handleToString(handle) << " ...");
1458
        obj = gObject->addObject();
5 andreas 1459
 
1460
        if (!obj)
1461
        {
1462
            MSG_ERROR("Error creating an object!");
1463
            TError::setError();
14 andreas 1464
            draw_mutex.unlock();
5 andreas 1465
            return;
1466
        }
1467
 
107 andreas 1468
        obj->type = TObject::OBJ_BUTTON;
5 andreas 1469
        obj->handle = handle;
38 andreas 1470
        obj->width = scale(width);
1471
        obj->height = scale(height);
1472
        obj->left = scale(left);
1473
        obj->top = scale(top);
40 andreas 1474
 
107 andreas 1475
        if (par->type == TObject::OBJ_PAGE)
40 andreas 1476
            obj->object.label = new QLabel("", mBackground);
1477
        else
1478
            obj->object.label = new QLabel("", par->object.widget);
1479
 
15 andreas 1480
        obj->object.label->installEventFilter(this);
57 andreas 1481
        obj->object.label->grabGesture(Qt::PinchGesture);
59 andreas 1482
        obj->object.label->setFixedSize(obj->width, obj->height);
1483
        obj->object.label->move(obj->left, obj->top);
1484
        obj->object.label->setAttribute(Qt::WA_TransparentForMouseEvents);
6 andreas 1485
    }
14 andreas 1486
    else
107 andreas 1487
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << TObject::objectToString(obj->type) << " found!");
5 andreas 1488
 
75 andreas 1489
    try
6 andreas 1490
    {
75 andreas 1491
        if (buffer.size() > 0 && pixline > 0)
1492
        {
107 andreas 1493
            MSG_DEBUG("Setting image for " << TObject::handleToString(handle) << " ...");
75 andreas 1494
            QImage img((unsigned char *)buffer.data(), width, height, pixline, QImage::Format_ARGB32);  // Original size
107 andreas 1495
 
1496
            if (img.isNull() || !img.valid(width-1, height-1))
1497
            {
1498
                MSG_ERROR("Unable to create a valid image!");
1499
                draw_mutex.unlock();
1500
                return;
1501
            }
1502
 
75 andreas 1503
            QSize size(obj->width, obj->height);
107 andreas 1504
            QPixmap pixmap;
1505
            bool ret = false;
38 andreas 1506
 
75 andreas 1507
            if (isScaled())
107 andreas 1508
                ret = pixmap.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Scaled size
75 andreas 1509
            else
107 andreas 1510
                ret = pixmap.convertFromImage(img);
38 andreas 1511
 
107 andreas 1512
            if (!ret || pixmap.isNull())
1513
            {
1514
                MSG_ERROR("Unable to create a pixmap out of an image!");
1515
                draw_mutex.unlock();
1516
                return;
1517
            }
1518
 
1519
            if (obj->object.label)
1520
                obj->object.label->setPixmap(pixmap);
1521
            else
1522
            {
1523
                MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist any more!");
1524
            }
75 andreas 1525
        }
1526
 
107 andreas 1527
        if (obj->object.label)
75 andreas 1528
            obj->object.label->show();
5 andreas 1529
    }
75 andreas 1530
    catch(std::exception& e)
31 andreas 1531
    {
107 andreas 1532
        MSG_ERROR("Error drawing button " << TObject::handleToString(handle) << ": " << e.what());
31 andreas 1533
    }
93 andreas 1534
    catch(...)
1535
    {
1536
        MSG_ERROR("Unexpected exception occured [MainWindow::displayButton()]");
1537
    }
75 andreas 1538
 
107 andreas 1539
    gObject->cleanMarked();     // We want to be sure to have no dead entries.
14 andreas 1540
    draw_mutex.unlock();
4 andreas 1541
}
1542
 
98 andreas 1543
void MainWindow::SetVisible(ulong handle, bool state)
1544
{
1545
    DECL_TRACER("MainWindow::SetVisible(ulong handle, bool state)");
1546
 
107 andreas 1547
    if (!gObject)
1548
    {
1549
        MSG_ERROR(_NO_OBJECT);
1550
        return;
1551
    }
98 andreas 1552
 
107 andreas 1553
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1554
 
98 andreas 1555
    if (!obj)
1556
    {
107 andreas 1557
        MSG_ERROR("Object " << TObject::handleToString(handle) << " not found!");
98 andreas 1558
        return;
1559
    }
1560
 
107 andreas 1561
    if (obj->type == TObject::OBJ_BUTTON && obj->object.label)
99 andreas 1562
    {
107 andreas 1563
        MSG_DEBUG("Setting object " << TObject::handleToString(handle) << " visibility to " << (state ? "TRUE" : "FALSE"));
98 andreas 1564
        obj->object.label->setVisible(state);
99 andreas 1565
    }
1566
    else
1567
    {
107 andreas 1568
        MSG_DEBUG("Ignoring non button object " << TObject::handleToString(handle));
99 andreas 1569
    }
98 andreas 1570
}
1571
 
5 andreas 1572
void MainWindow::setPage(ulong handle, int width, int height)
1573
{
1574
    DECL_TRACER("MainWindow::setPage(ulong handle, int width, int height)");
4 andreas 1575
 
107 andreas 1576
    if (!gObject)
1577
    {
1578
        MSG_ERROR(_NO_OBJECT);
1579
        return;
1580
    }
1581
 
1582
    draw_mutex.lock();
1583
 
6 andreas 1584
    QSize qs = menuBar()->sizeHint();
38 andreas 1585
    this->setMinimumSize(scale(width), scale(height) + qs.height());
107 andreas 1586
    TObject::OBJECT_t *obj = gObject->findObject(handle);
5 andreas 1587
 
1588
    if (!obj)
1589
    {
107 andreas 1590
        obj = gObject->addObject();
5 andreas 1591
 
1592
        if (!obj)
1593
        {
107 andreas 1594
            MSG_ERROR("Error crating an object for handle " << TObject::handleToString(handle));
5 andreas 1595
            TError::setError();
14 andreas 1596
            draw_mutex.unlock();
5 andreas 1597
            return;
1598
        }
1599
 
1600
        obj->handle = handle;
38 andreas 1601
        obj->height = scale(height);
1602
        obj->width = scale(width);
107 andreas 1603
        obj->type = TObject::OBJ_PAGE;
5 andreas 1604
    }
6 andreas 1605
 
1606
    bool newBackground = false;
1607
 
1608
    if (!mBackground)
1609
    {
1610
        mBackground = new QWidget();
57 andreas 1611
        mBackground->grabGesture(Qt::PinchGesture);
6 andreas 1612
        mBackground->setAutoFillBackground(true);
31 andreas 1613
        mBackground->setBackgroundRole(QPalette::Window);
58 andreas 1614
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
6 andreas 1615
        mBackground->setFixedSize(obj->width, obj->height);
40 andreas 1616
#else
10 andreas 1617
        QRect rectBack = mBackground->geometry();
1618
        QRect rectMain = this->geometry();
40 andreas 1619
 
1620
        QSize icSize =  this->iconSize();
90 andreas 1621
        int width = rectMain.width() + icSize.width() + 16;
40 andreas 1622
        rectMain.setWidth(width);
1623
        setGeometry(rectMain);
1624
        // If our first top pixel is not 0, maybe because of a menu, window
1625
        // decorations or a toolbar, we must add this extra height to the
1626
        // positions of widgets and mouse presses.
28 andreas 1627
        int avHeight = rectMain.height() - rectBack.height();
1628
        MSG_DEBUG("avHeight=" << avHeight);
92 andreas 1629
        gPageManager->setFirstTopPixel(avHeight);
31 andreas 1630
#endif
6 andreas 1631
        newBackground = true;
1632
    }
1633
 
1634
    // By default set a transparent background
38 andreas 1635
    QPixmap pix(obj->width, obj->height);
6 andreas 1636
    pix.fill(QColor::fromRgba(qRgba(0,0,0,0xff)));
1637
    QPalette palette;
1638
    palette.setBrush(QPalette::Window, pix);
1639
    mBackground->setPalette(palette);
1640
 
1641
    if (newBackground)
1642
        this->setCentralWidget(mBackground);
1643
 
62 andreas 1644
    mBackground->show();
14 andreas 1645
    draw_mutex.unlock();
5 andreas 1646
}
1647
 
41 andreas 1648
void MainWindow::setSubPage(ulong handle, int left, int top, int width, int height, ANIMATION_t animate)
5 andreas 1649
{
14 andreas 1650
    draw_mutex.lock();
5 andreas 1651
    DECL_TRACER("MainWindow::setSubPage(ulong handle, int left, int top, int width, int height)");
38 andreas 1652
 
107 andreas 1653
    if (!gObject)
1654
    {
1655
        MSG_ERROR(_NO_OBJECT);
1656
        draw_mutex.unlock();
1657
        return;
1658
    }
1659
 
38 andreas 1660
    if (isScaled())
1661
    {
1662
        MSG_DEBUG("Scaling to factor " << mScaleFactor);
1663
    }
1664
 
107 andreas 1665
    TObject::OBJECT_t *obj = gObject->addObject();
5 andreas 1666
 
1667
    if (!obj)
1668
    {
1669
        MSG_ERROR("Error adding an object!");
1670
        TError::setError();
14 andreas 1671
        draw_mutex.unlock();
5 andreas 1672
        return;
1673
    }
1674
 
38 andreas 1675
    int scLeft = scale(left);
1676
    int scTop = scale(top);
1677
    int scWidth = scale(width);
1678
    int scHeight = scale(height);
1679
 
107 andreas 1680
    obj->type = TObject::OBJ_SUBPAGE;
5 andreas 1681
    obj->handle = handle;
13 andreas 1682
    obj->object.widget = new QWidget(centralWidget());
6 andreas 1683
    obj->object.widget->setAutoFillBackground(true);
38 andreas 1684
    obj->object.widget->setFixedSize(scWidth, scHeight);
1685
    obj->object.widget->move(scLeft, scTop);
1686
    obj->left = scLeft;
1687
    obj->top = scTop;
1688
    obj->width = scWidth;
1689
    obj->height = scHeight;
15 andreas 1690
    // filter move event
1691
    obj->object.widget->installEventFilter(this);
57 andreas 1692
    obj->object.widget->grabGesture(Qt::PinchGesture);
5 andreas 1693
    // By default set a transparent background
38 andreas 1694
    QPixmap pix(scWidth, scHeight);
5 andreas 1695
    pix.fill(QColor::fromRgba(qRgba(0,0,0,0xff)));
1696
    QPalette palette;
18 andreas 1697
    palette.setBrush(QPalette::Window, QBrush(pix));
5 andreas 1698
    obj->object.widget->setPalette(palette);
107 andreas 1699
    obj->aniDirection = true;
31 andreas 1700
 
107 andreas 1701
    startAnimation(obj, animate);
14 andreas 1702
    draw_mutex.unlock();
5 andreas 1703
}
1704
 
38 andreas 1705
void MainWindow::setBackground(ulong handle, QByteArray image, size_t rowBytes, int width, int height, ulong color)
5 andreas 1706
{
14 andreas 1707
    draw_mutex.lock();
28 andreas 1708
    DECL_TRACER("MainWindow::setBackground(ulong handle, QByteArray image, size_t rowBytes, ulong color)");
5 andreas 1709
 
107 andreas 1710
    if (!gObject)
1711
    {
1712
        MSG_ERROR(_NO_OBJECT);
1713
        draw_mutex.unlock();
1714
        return;
1715
    }
5 andreas 1716
 
107 andreas 1717
 
1718
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1719
 
5 andreas 1720
    if (!obj)
1721
    {
107 andreas 1722
        MSG_WARNING("No object " << TObject::handleToString(handle) << " found!");
14 andreas 1723
        draw_mutex.unlock();
5 andreas 1724
        return;
1725
    }
1726
 
107 andreas 1727
    MSG_TRACE("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
5 andreas 1728
 
107 andreas 1729
    if (obj->type == TObject::OBJ_BUTTON || obj->type == TObject::OBJ_SUBPAGE)
5 andreas 1730
    {
107 andreas 1731
        MSG_DEBUG("Processing object " << gObject->objectToString(obj->type));
6 andreas 1732
        QPixmap pix(obj->width, obj->height);
1733
        pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));
5 andreas 1734
 
14 andreas 1735
        if (image.size() > 0)
5 andreas 1736
        {
38 andreas 1737
            MSG_DEBUG("Setting image of size " << image.size() << " (" << width << " x " << height << ")");
1738
            QImage img((unsigned char *)image.data(), width, height, rowBytes, QImage::Format_ARGB32);
1739
 
1740
            if (isScaled())
1741
            {
1742
                QSize size(obj->width, obj->height);
1743
                pix.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
1744
            }
1745
            else
1746
                pix.convertFromImage(img);
6 andreas 1747
        }
1748
 
107 andreas 1749
        if (obj->type == TObject::OBJ_BUTTON)
6 andreas 1750
        {
5 andreas 1751
            obj->object.label->setPixmap(pix);
31 andreas 1752
 
1753
            if (mHasFocus)
1754
                obj->object.label->show();
5 andreas 1755
        }
6 andreas 1756
        else
5 andreas 1757
        {
19 andreas 1758
            MSG_DEBUG("Setting image as background for page " << ((handle >> 16) & 0x0000ffff));
5 andreas 1759
            QPalette palette;
18 andreas 1760
            palette.setBrush(QPalette::Window, QBrush(pix));
6 andreas 1761
            obj->object.widget->setPalette(palette);
31 andreas 1762
 
107 andreas 1763
            obj->object.widget->show();
5 andreas 1764
        }
1765
    }
107 andreas 1766
    else if (obj->type == TObject::OBJ_PAGE)
5 andreas 1767
    {
6 andreas 1768
        bool newBackground = false;
5 andreas 1769
 
6 andreas 1770
        if (!mBackground)
5 andreas 1771
        {
6 andreas 1772
            mBackground = new QWidget();
1773
            mBackground->setAutoFillBackground(true);
31 andreas 1774
            mBackground->setBackgroundRole(QPalette::Window);
6 andreas 1775
            mBackground->setFixedSize(obj->width, obj->height);
1776
            newBackground = true;
1777
            MSG_DEBUG("New background image added to page with size " << obj->width << " x " << obj->height);
5 andreas 1778
        }
6 andreas 1779
 
1780
        QPixmap pix(obj->width, obj->height);
1781
        pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));
1782
 
14 andreas 1783
        if (image.size() > 0)
5 andreas 1784
        {
38 andreas 1785
            QImage img((unsigned char *)image.data(), width, height, rowBytes, QImage::Format_ARGB32);
1786
 
1787
            if (isScaled())
1788
                pix.convertFromImage(img.scaled(obj->width, obj->height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
1789
            else
1790
                pix.convertFromImage(img);
1791
 
26 andreas 1792
            MSG_DEBUG("Image converted. Width=" << obj->width << ", Height=" << obj->height);
6 andreas 1793
        }
5 andreas 1794
 
6 andreas 1795
        QPalette palette;
18 andreas 1796
        palette.setBrush(QPalette::Window, QBrush(pix));
6 andreas 1797
        mBackground->setPalette(palette);
1798
 
1799
        if (newBackground)
1800
            this->setCentralWidget(mBackground);
1801
 
31 andreas 1802
        if (mHasFocus)
1803
            mBackground->show();
1804
 
6 andreas 1805
        MSG_DEBUG("Background set");
5 andreas 1806
    }
14 andreas 1807
 
1808
    draw_mutex.unlock();
5 andreas 1809
}
1810
 
11 andreas 1811
void MainWindow::dropPage(ulong handle)
1812
{
14 andreas 1813
    draw_mutex.lock();
11 andreas 1814
    DECL_TRACER("MainWindow::dropPage(ulong handle)");
7 andreas 1815
 
107 andreas 1816
    if (!gObject)
1817
    {
1818
        MSG_ERROR(_NO_OBJECT);
1819
        draw_mutex.unlock();
1820
        return;
1821
    }
11 andreas 1822
 
107 andreas 1823
    gObject->removeAllChilds(handle);
1824
    gObject->removeObject(handle);
1825
 
11 andreas 1826
    if (mBackground)
1827
    {
1828
        delete mBackground;
1829
        mBackground = nullptr;
1830
    }
14 andreas 1831
 
1832
    draw_mutex.unlock();
11 andreas 1833
}
1834
 
1835
void MainWindow::dropSubPage(ulong handle)
1836
{
14 andreas 1837
    draw_mutex.lock();
11 andreas 1838
    DECL_TRACER("MainWindow::dropSubPage(ulong handle)");
1839
 
107 andreas 1840
    if (!gObject)
1841
    {
1842
        MSG_ERROR(_NO_OBJECT);
1843
        draw_mutex.unlock();
1844
        return;
1845
    }
11 andreas 1846
 
107 andreas 1847
    gObject->removeAllChilds(handle);
1848
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1849
 
11 andreas 1850
    if (!obj)
1851
    {
107 andreas 1852
        MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist. Ignoring!");
14 andreas 1853
        draw_mutex.unlock();
11 andreas 1854
        return;
1855
    }
1856
 
107 andreas 1857
    obj->aniDirection = false;
42 andreas 1858
    startAnimation(obj, obj->animate, false);
107 andreas 1859
    TObject::OBJECT_t *o = mLastObject;
43 andreas 1860
 
42 andreas 1861
    if (obj->animate.hideEffect == SE_NONE || !o)
1862
    {
107 andreas 1863
        gObject->dropContent(obj);
1864
        gObject->removeObject(handle);
42 andreas 1865
    }
1866
 
14 andreas 1867
    draw_mutex.unlock();
11 andreas 1868
}
1869
 
98 andreas 1870
void MainWindow::dropButton(ulong handle)
1871
{
1872
    draw_mutex.lock();
1873
    DECL_TRACER("MainWindow::dropButton(ulong handle)");
1874
 
107 andreas 1875
    if (!gObject)
1876
    {
1877
        MSG_ERROR(_NO_OBJECT);
1878
        draw_mutex.unlock();
1879
        return;
1880
    }
98 andreas 1881
 
107 andreas 1882
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1883
 
98 andreas 1884
    if (!obj)
1885
    {
107 andreas 1886
        MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist. Ignoring!");
98 andreas 1887
        draw_mutex.unlock();
1888
        return;
1889
    }
1890
 
107 andreas 1891
    if (obj->type == TObject::OBJ_BUTTON && obj->object.label)
98 andreas 1892
    {
1893
        obj->object.label->close();
1894
        obj->object.label = nullptr;
1895
    }
1896
 
107 andreas 1897
    gObject->removeObject(handle);
98 andreas 1898
    draw_mutex.unlock();
1899
}
1900
 
21 andreas 1901
void MainWindow::playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
1902
{
1903
    draw_mutex.lock();
1904
    DECL_TRACER("MainWindow::playVideo(ulong handle, const string& url, const string& user, const string& pw))");
1905
 
107 andreas 1906
    if (!gObject)
1907
    {
1908
        MSG_ERROR(_NO_OBJECT);
1909
        draw_mutex.unlock();
1910
        return;
1911
    }
21 andreas 1912
 
107 andreas 1913
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1914
    TObject::OBJECT_t *par = gObject->findObject(parent);
1915
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << TObject::handleToString(parent));
1916
 
21 andreas 1917
    if (!par)
1918
    {
1919
        MSG_WARNING("Button has no parent! Ignoring it.");
1920
        draw_mutex.unlock();
1921
        return;
1922
    }
1923
 
1924
    if (!obj)
1925
    {
1926
        MSG_DEBUG("Adding new video object ...");
107 andreas 1927
        obj = gObject->addObject();
21 andreas 1928
 
1929
        if (!obj)
1930
        {
1931
            MSG_ERROR("Error creating a video object!");
1932
            TError::setError();
1933
            draw_mutex.unlock();
1934
            return;
1935
        }
1936
 
107 andreas 1937
        obj->type = TObject::OBJ_VIDEO;
21 andreas 1938
        obj->handle = handle;
1939
        obj->width = width;
1940
        obj->height = height;
1941
        obj->left = left;
1942
        obj->top = top;
1943
        obj->object.vwidget = new QVideoWidget(par->object.widget);
1944
        obj->object.vwidget->installEventFilter(this);
1945
    }
1946
    else
107 andreas 1947
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
21 andreas 1948
 
1949
    QMediaPlaylist *playlist = new QMediaPlaylist;
1950
    QUrl qurl(url.c_str());
1951
 
1952
    if (!user.empty())
1953
        qurl.setUserName(user.c_str());
1954
 
1955
    if (!pw.empty())
1956
        qurl.setPassword(pw.c_str());
1957
 
1958
    playlist->addMedia(qurl);
1959
    obj->player = new QMediaPlayer;
1960
    obj->player->setPlaylist(playlist);
1961
    obj->player->setVideoOutput(obj->object.vwidget);
31 andreas 1962
 
107 andreas 1963
    obj->object.vwidget->show();
1964
    obj->player->play();
21 andreas 1965
}
1966
 
52 andreas 1967
void MainWindow::inputText(Button::TButton* button, QByteArray buf, int width, int height, size_t pixline)
50 andreas 1968
{
1969
    DECL_TRACER("MainWindow::inputText(Button::TButton* button)");
1970
 
107 andreas 1971
    if (!gObject)
1972
    {
1973
        MSG_ERROR(_NO_OBJECT);
1974
        return;
1975
    }
1976
 
50 andreas 1977
    if (!button)
1978
    {
1979
        MSG_WARNING("No valid button!");
1980
        return;
1981
    }
1982
 
1983
    ulong handle = button->getHandle();
1984
    ulong parent = button->getParent();
107 andreas 1985
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1986
    TObject::OBJECT_t *par = gObject->findObject(parent);
1987
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << gObject->handleToString(parent));
50 andreas 1988
 
1989
    if (!par)
1990
    {
1991
        MSG_WARNING("Button has no parent! Ignoring it.");
1992
        return;
1993
    }
1994
 
1995
    if (!obj)
1996
    {
1997
        MSG_DEBUG("Adding new input object ...");
107 andreas 1998
        obj = gObject->addObject();
50 andreas 1999
 
2000
        if (!obj)
2001
        {
2002
            MSG_ERROR("Error creating an input object!");
2003
            TError::setError();
2004
            return;
2005
        }
2006
 
107 andreas 2007
        obj->type = TObject::OBJ_INPUT;
50 andreas 2008
        obj->handle = handle;
52 andreas 2009
        obj->width = scale(width);
2010
        obj->height = scale(height);
50 andreas 2011
        obj->left = scale(button->getLeftPosition());
2012
        obj->top = scale(button->getTopPosition());
51 andreas 2013
 
2014
        if (button->isSingleLine())
2015
        {
2016
            obj->object.linetext = new QLineEdit(button->getText().c_str(), par->object.widget);
2017
            obj->object.linetext->setFixedSize(obj->width, obj->height);
2018
            obj->object.linetext->move(obj->left, obj->top);
52 andreas 2019
//            obj->object.linetext->setAutoFillBackground(true);
51 andreas 2020
            obj->object.linetext->installEventFilter(this);
2021
            obj->object.linetext->connect(obj->object.linetext, &QLineEdit::editingFinished, this, &MainWindow::textSingleLineReturn);
2022
            obj->wid = obj->object.linetext->winId();
2023
        }
2024
        else
2025
        {
2026
            obj->object.multitext = new QTextEdit(button->getText().c_str(), par->object.widget);
2027
            obj->object.multitext->setFixedSize(obj->width, obj->height);
2028
            obj->object.multitext->move(obj->left, obj->top);
52 andreas 2029
//            obj->object.multitext->setAutoFillBackground(true);
51 andreas 2030
            obj->object.multitext->installEventFilter(this);
2031
            obj->object.multitext->connect(obj->object.multitext, &QTextEdit::textChanged, this, &MainWindow::textChangedMultiLine);
2032
            obj->wid = obj->object.multitext->winId();
2033
        }
2034
 
52 andreas 2035
        if (!buf.size() || pixline == 0)
51 andreas 2036
        {
2037
            MSG_ERROR("No image!");
2038
            TError::setError();
2039
            return;
2040
        }
2041
 
52 andreas 2042
        MSG_DEBUG("Background image size: " << width << " x " << height << ", rowBytes: " << pixline);
2043
        QPixmap pix(width, height);
2044
        QImage img((uchar *)buf.data(), width, height, QImage::Format_ARGB32);
50 andreas 2045
 
2046
        if (isScaled())
52 andreas 2047
            pix.convertFromImage(img.scaled(scale(width), scale(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
50 andreas 2048
        else
2049
            pix.convertFromImage(img);
2050
 
2051
        // Load the font
2052
        FONT_T font = button->getFont();
2053
        int fontID = 0;
51 andreas 2054
        vector<string> fontList = TFont::getFontPathList();
2055
        vector<string>::iterator iter;
2056
        string ffile;
50 andreas 2057
 
51 andreas 2058
        for (iter = fontList.begin(); iter != fontList.end(); ++iter)
50 andreas 2059
        {
51 andreas 2060
            TValidateFile vf;
50 andreas 2061
 
51 andreas 2062
            if (!vf.isValidFile(*iter + "/" + font.file))
2063
                continue;
2064
 
2065
            ffile = *iter + "/" + font.file;
2066
            break;
50 andreas 2067
        }
2068
 
51 andreas 2069
        if (ffile.empty())
2070
        {
2071
            MSG_ERROR("Font " << font.file << " doesn't exists!");
2072
            return;
2073
        }
2074
 
50 andreas 2075
        if ((fontID = QFontDatabase::addApplicationFont(ffile.c_str())) == -1)
2076
        {
2077
            MSG_ERROR("Font " << ffile << " could not be loaded!");
2078
            TError::setError();
2079
            return;
2080
        }
2081
 
2082
        QFont ft;
2083
        ft.setFamily(font.name.c_str());
2084
        ft.setPointSize(font.size);
51 andreas 2085
        MSG_DEBUG("Using font \"" << font.name << "\" with size " << font.size << "pt.");
50 andreas 2086
 
2087
        switch (button->getFontStyle())
2088
        {
2089
            case FONT_BOLD:     ft.setBold(true); break;
2090
            case FONT_ITALIC:   ft.setItalic(true); break;
2091
            case FONT_BOLD_ITALIC:
2092
                ft.setBold(true);
2093
                ft.setItalic(true);
2094
                break;
2095
 
2096
            default:
2097
                ft.setBold(false);
2098
                ft.setItalic(false);
2099
        }
2100
 
52 andreas 2101
        QPalette palette;
50 andreas 2102
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor());
52 andreas 2103
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor());
51 andreas 2104
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
52 andreas 2105
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
2106
        palette.setColor(QPalette::Base, cfcolor);
51 andreas 2107
        palette.setColor(QPalette::Text, txcolor);
52 andreas 2108
        //        pix.save("frame.png");
51 andreas 2109
 
2110
        if (button->isSingleLine())
2111
        {
2112
            MSG_DEBUG("Initializing a single line ...");
53 andreas 2113
//            palette.setBrush(QPalette::Base, QBrush(pix));
52 andreas 2114
            obj->object.linetext->setFont(ft);
51 andreas 2115
            obj->object.linetext->setPalette(palette);
2116
            obj->object.linetext->setMaxLength(button->getTextMaxChars());
52 andreas 2117
//            obj->object.linetext->setFrame(false);
51 andreas 2118
            obj->object.linetext->setText(button->getText().c_str());
2119
            obj->object.linetext->show();
2120
        }
2121
        else
2122
        {
2123
            MSG_DEBUG("Initializing a multiline text area ...");
53 andreas 2124
            palette.setBrush(QPalette::Base, QBrush(pix));
51 andreas 2125
            obj->object.multitext->setPalette(palette);
2126
            obj->object.multitext->setFont(ft);
2127
            obj->object.multitext->setAcceptRichText(false);
2128
            obj->object.multitext->setText(button->getText().c_str());
2129
 
2130
            if (button->getTextWordWrap())
2131
                obj->object.multitext->setWordWrapMode(QTextOption::WordWrap);
2132
            else
2133
                obj->object.multitext->setWordWrapMode(QTextOption::NoWrap);
2134
 
2135
            obj->object.multitext->show();
2136
        }
50 andreas 2137
    }
2138
    else
107 andreas 2139
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
50 andreas 2140
}
62 andreas 2141
 
63 andreas 2142
void MainWindow::showKeyboard(const std::string& init, const std::string& prompt, bool priv)
31 andreas 2143
{
63 andreas 2144
    DECL_TRACER("MainWindow::showKeyboard(std::string &init, std::string &prompt, bool priv)");
31 andreas 2145
 
63 andreas 2146
    if (mKeyboard)
2147
        return;
2148
 
2149
    mQKeyboard = new TQKeyboard(init, prompt, this);
2150
    mKeyboard = true;
62 andreas 2151
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
65 andreas 2152
    mQKeyboard->setScaleFactor(mScaleFactor);
62 andreas 2153
#endif
63 andreas 2154
    mQKeyboard->setPrivate(priv);
2155
    mQKeyboard->doResize();
2156
    mQKeyboard->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
2157
    int ret = mQKeyboard->exec();
31 andreas 2158
 
62 andreas 2159
    if (ret == QDialog::Accepted)
2160
    {
2161
        string text = "KEYB-";
63 andreas 2162
        text.append(mQKeyboard->getText());
62 andreas 2163
 
2164
        if (gPageManager)
2165
            gPageManager->sendKeyboard(text);
2166
    }
2167
    else
2168
    {
2169
        string text = "KEYB-ABORT";
2170
 
2171
        if (gPageManager)
2172
            gPageManager->sendKeyboard(text);
2173
    }
2174
 
63 andreas 2175
    delete mQKeyboard;
2176
    mQKeyboard = nullptr;
2177
    mKeyboard = false;
31 andreas 2178
}
62 andreas 2179
 
63 andreas 2180
void MainWindow::showKeypad(const std::string& init, const std::string& prompt, bool priv)
62 andreas 2181
{
63 andreas 2182
    DECL_TRACER("MainWindow::showKeypad(std::string& init, std::string& prompt, bool priv)");
62 andreas 2183
 
65 andreas 2184
    if (mKeypad)
63 andreas 2185
        return;
2186
 
2187
    mQKeypad = new TQKeypad(init, prompt, this);
65 andreas 2188
    mKeypad = true;
62 andreas 2189
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
63 andreas 2190
    mQKeypad->setScaleFactor(mScaleFactor);
65 andreas 2191
#endif
2192
    mQKeypad->setPrivate(priv);
111 andreas 2193
    mQKeypad->setMaxLength(50);     // Standard maximum length
63 andreas 2194
    mQKeypad->doResize();
2195
    mQKeypad->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
65 andreas 2196
    int ret = mQKeypad->exec();
62 andreas 2197
 
2198
    if (ret == QDialog::Accepted)
2199
    {
2200
        string text = "KEYP-";
63 andreas 2201
        text.append(mQKeypad->getText());
62 andreas 2202
 
2203
        if (gPageManager)
2204
            gPageManager->sendKeypad(text);
2205
    }
2206
    else
2207
    {
2208
        string text = "KEYP-ABORT";
2209
 
2210
        if (gPageManager)
2211
            gPageManager->sendKeypad(text);
2212
    }
2213
 
63 andreas 2214
    delete mQKeypad;
2215
    mQKeypad = nullptr;
65 andreas 2216
    mKeypad = false;
62 andreas 2217
}
2218
 
63 andreas 2219
void MainWindow::resetKeyboard()
2220
{
2221
    DECL_TRACER("MainWindow::resetKeyboard()");
2222
 
2223
    if (mQKeyboard)
2224
        mQKeyboard->reject();
2225
 
2226
    if (mQKeypad)
2227
        mQKeyboard->reject();
2228
}
2229
 
111 andreas 2230
void MainWindow::sendVirtualKeys(const string& str)
2231
{
2232
    DECL_TRACER("MainWindow::sendVirtualKeys(const string& str)");
2233
 
2234
    if (mKeyboard && mQKeyboard)
2235
        mQKeyboard->setString(str);
2236
    else if (mKeypad && mQKeypad)
2237
        mQKeypad->setString(str);
2238
}
2239
 
64 andreas 2240
void MainWindow::showSetup()
2241
{
2242
    DECL_TRACER("MainWindow::showSetup()");
2243
 
2244
    settings();
2245
}
2246
 
71 andreas 2247
void MainWindow::playSound(const string& file)
2248
{
2249
    DECL_TRACER("MainWindow::playSound(const string& file)");
2250
 
2251
    MSG_DEBUG("Playing file " << file);
2252
    QSound::play(file.c_str());
2253
}
2254
 
31 andreas 2255
void MainWindow::playShowList()
2256
{
2257
    DECL_TRACER("MainWindow::playShowList()");
2258
 
61 andreas 2259
    _EMIT_TYPE_t etype = getNextType();
2260
 
2261
    while (etype != ET_NONE)
2262
    {
2263
        ulong handle = 0;
2264
        ulong parent = 0;
2265
        unsigned char *buffer;
2266
        int pixline = 0;
2267
        int left = 0;
2268
        int top = 0;
2269
        int width = 0;
2270
        int height = 0;
2271
        unsigned char *image;
2272
        size_t size = 0;
2273
        size_t rowBytes = 0;
2274
        ulong color = 0;
2275
        ANIMATION_t animate;
2276
        std::string url;
2277
        std::string user;
2278
        std::string pw;
2279
        Button::TButton *button;
2280
        Button::BITMAP_t bm;
2281
 
2282
        switch(etype)
2283
        {
2284
            case ET_BACKGROUND:
2285
                if (getBackground(&handle, &image, &size, &rowBytes, &width, &height, &color))
2286
                {
2287
                    QByteArray buf;
2288
 
2289
                    if (image && size > 0)
2290
                        buf.insert(0, (const char *)image, size);
2291
 
2292
                    emit sigSetBackground(handle, buf, rowBytes, width, height, color);
2293
                }
2294
            break;
2295
 
2296
            case ET_BUTTON:
2297
                if (getButton(&handle, &parent, &buffer, &pixline, &left, &top, &width, &height))
2298
                {
2299
                    QByteArray buf;
2300
 
2301
                    if (buffer && pixline > 0)
2302
                    {
2303
                        size_t size = width * height * (pixline / width);
2304
                        MSG_DEBUG("Buffer size=" << size << ", width=" << width << ", height=" << height << ", left=" << left << ", top=" << top);
2305
                        buf.insert(0, (const char *)buffer, size);
2306
                    }
2307
 
2308
                    emit sigDisplayButton(handle, parent, buf, width, height, pixline, left, top);
2309
                }
2310
            break;
2311
 
2312
            case ET_INTEXT:
2313
                if (getInText(&handle, &button, &bm))
2314
                {
2315
                    QByteArray buf;
2316
 
2317
                    if (bm.buffer && bm.rowBytes > 0)
2318
                    {
2319
                        size_t size = bm.width * bm.height * (bm.rowBytes / bm.width);
2320
                        buf.insert(0, (const char *)bm.buffer, size);
2321
                    }
2322
 
2323
                    emit sigInputText(button, buf, bm.width, bm.height, bm.rowBytes);
2324
                }
2325
            break;
2326
 
2327
            case ET_PAGE:
2328
                if (getPage(&handle, &width, &height))
2329
                {
2330
                    if (isDeleted())
2331
                        emit sigDropPage(handle);
2332
                    else
2333
                        emit sigSetPage(handle, width, height);
2334
                }
2335
            break;
2336
 
2337
            case ET_SUBPAGE:
2338
                if (getSubPage(&handle, &left, &top, &width, &height, &animate))
2339
                {
2340
                    if (isDeleted())
2341
                        emit sigDropSubPage(handle);
2342
                    else
2343
                        emit sigSetSubPage(handle, left, top, width, height, animate);
2344
                }
2345
            break;
2346
 
2347
            case ET_VIDEO:
2348
                if (getVideo(&handle, &parent, &left, &top, &width, &height, &url, &user, &pw))
2349
                {
2350
                    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
2351
                }
2352
            break;
2353
 
2354
            default:
2355
                MSG_WARNING("Type " << etype << " is currently not supported!");
2356
        }
2357
 
2358
        dropType(etype);
2359
        etype = getNextType();
2360
    }
2361
/*
31 andreas 2362
    std::map<ulong, QWidget *>::iterator iter;
2363
 
2364
    for (iter = mToShow.begin(); iter != mToShow.end(); ++iter)
2365
    {
2366
        OBJECT_t *obj = findObject(iter->first);
2367
 
2368
        if (obj)
2369
            iter->second->show();
2370
    }
2371
 
2372
    mToShow.clear();
61 andreas 2373
*/
31 andreas 2374
}
2375
 
42 andreas 2376
 
38 andreas 2377
int MainWindow::scale(int value)
2378
{
2379
    if (value <= 0 || mScaleFactor == 1.0)
2380
        return value;
2381
 
2382
    return (int)((double)value * mScaleFactor);
2383
}
2384
 
107 andreas 2385
void MainWindow::startAnimation(TObject::OBJECT_t* obj, ANIMATION_t& ani, bool in)
42 andreas 2386
{
2387
    DECL_TRACER("MainWindow::startAnimation(OBJECT_t* obj, ANIMATION_t& ani)");
43 andreas 2388
 
107 andreas 2389
    if (!obj)
2390
    {
2391
        MSG_ERROR("Got no object to start the animation!");
2392
        return;
2393
    }
2394
 
42 andreas 2395
    SHOWEFFECT_t effect;
2396
    int scLeft = obj->left;
2397
    int scTop = obj->top;
2398
    int scWidth = obj->width;
2399
    int scHeight = obj->height;
2400
    mLastObject = nullptr;
43 andreas 2401
 
42 andreas 2402
    obj->animate = ani;
43 andreas 2403
 
42 andreas 2404
    if (in)
2405
        effect = ani.showEffect;
2406
    else
2407
        effect = ani.hideEffect;
2408
 
54 andreas 2409
    if (effect == SE_NONE)
2410
        return;
43 andreas 2411
 
58 andreas 2412
    if (effect == SE_FADE)
2413
    {
107 andreas 2414
        MSG_DEBUG("Fading object " << TObject::handleToString(obj->handle) << (in ? " IN" : " OUT"));
58 andreas 2415
        QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(obj->object.widget);
2416
        obj->object.widget->setGraphicsEffect(effect);
2417
        obj->animation = new QPropertyAnimation(effect, "opacity");
2418
    }
2419
    else
2420
    {
107 andreas 2421
        MSG_DEBUG("Moving object " << TObject::handleToString(obj->handle) << (in ? " IN" : " OUT"));
58 andreas 2422
        obj->animation = new QPropertyAnimation(obj->object.widget);
2423
        obj->animation->setTargetObject(obj->object.widget);
2424
    }
42 andreas 2425
 
54 andreas 2426
    if (in)
101 andreas 2427
        obj->animation->setDuration(ani.showTime * 100);    // convert 10th of seconds into milliseconds
54 andreas 2428
    else
101 andreas 2429
        obj->animation->setDuration(ani.hideTime * 100);    // convert 10th of seconds into milliseconds
43 andreas 2430
 
54 andreas 2431
    switch(effect)
2432
    {
2433
        case SE_SLIDE_BOTTOM_FADE:
2434
        case SE_SLIDE_BOTTOM:
2435
            obj->animation->setPropertyName("geometry");
42 andreas 2436
 
54 andreas 2437
            if (in)
2438
            {
2439
                obj->animation->setStartValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
2440
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
2441
            }
2442
            else
2443
            {
2444
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
2445
                obj->animation->setEndValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
2446
                obj->remove = true;
2447
                mLastObject = obj;
2448
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2449
            }
42 andreas 2450
 
54 andreas 2451
            obj->animation->start();
2452
        break;
43 andreas 2453
 
54 andreas 2454
        case SE_SLIDE_LEFT_FADE:
2455
        case SE_SLIDE_LEFT:
2456
            obj->animation->setPropertyName("geometry");
43 andreas 2457
 
54 andreas 2458
            if (in)
2459
            {
2460
                obj->animation->setStartValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
2461
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
2462
            }
2463
            else
2464
            {
2465
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
2466
                obj->animation->setEndValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
2467
                obj->remove = true;
2468
                mLastObject = obj;
2469
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2470
            }
42 andreas 2471
 
54 andreas 2472
            obj->animation->start();
2473
        break;
43 andreas 2474
 
54 andreas 2475
        case SE_SLIDE_RIGHT_FADE:
2476
        case SE_SLIDE_RIGHT:
2477
            obj->animation->setPropertyName("geometry");
43 andreas 2478
 
54 andreas 2479
            if (in)
2480
            {
2481
                obj->animation->setStartValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
2482
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
2483
            }
2484
            else
2485
            {
2486
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
2487
                obj->animation->setEndValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
2488
                obj->remove = true;
2489
                mLastObject = obj;
2490
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2491
            }
42 andreas 2492
 
54 andreas 2493
            obj->animation->start();
2494
        break;
43 andreas 2495
 
54 andreas 2496
        case SE_SLIDE_TOP_FADE:
2497
        case SE_SLIDE_TOP:
2498
            obj->animation->setPropertyName("geometry");
43 andreas 2499
 
54 andreas 2500
            if (in)
2501
            {
2502
                obj->animation->setStartValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
2503
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
2504
            }
2505
            else
2506
            {
2507
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
2508
                obj->animation->setEndValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
2509
                obj->remove = true;
2510
                mLastObject = obj;
2511
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2512
            }
43 andreas 2513
 
54 andreas 2514
            obj->animation->start();
2515
        break;
2516
 
58 andreas 2517
        case SE_FADE:
2518
            if (in)
2519
            {
2520
                obj->object.widget->setWindowOpacity(0.0);
2521
                obj->object.widget->show();
2522
                obj->animation->setStartValue(0.0);
2523
                obj->animation->setEndValue(1.0);
2524
            }
2525
            else
2526
            {
2527
                obj->animation->setStartValue(1.0);
2528
                obj->animation->setEndValue(0.0);
2529
                obj->remove = true;
2530
                mLastObject = obj;
2531
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2532
            }
2533
 
2534
            obj->animation->setEasingCurve(QEasingCurve::Linear);
2535
            obj->animation->start();
2536
        break;
2537
 
54 andreas 2538
        default:
2539
            MSG_WARNING("Subpage effect " << ani.showEffect << " is not supported.");
42 andreas 2540
    }
2541
}
2542
 
2 andreas 2543
#ifndef QT_NO_SESSIONMANAGER
2544
void MainWindow::commitData(QSessionManager &manager)
2545
{
5 andreas 2546
    if (manager.allowsInteraction())
2547
    {
2548
        if (!settingsChanged)
2549
            manager.cancel();
2550
    }
2551
    else
2552
    {
2553
        if (settingsChanged)
2554
            writeSettings();
2555
    }
2 andreas 2556
}
5 andreas 2557
#endif