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