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