Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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