Subversion Repositories tpanel

Rev

Rev 482 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
2
 * Copyright (C) 2020 to 2024 by Andreas Theofilu <andreas@theosys.at>
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
 
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
 */
28
#include <QApplication>
29
#include <QGuiApplication>
30
#include <QByteArray>
31
#include <QCommandLineParser>
32
#include <QCommandLineOption>
33
#include <QLabel>
34
#include <QtWidgets>
35
#include <QStackedWidget>
36
#include <QMouseEvent>
37
#include <QMoveEvent>
38
#include <QTouchEvent>
39
#include <QPalette>
40
#include <QPixmap>
41
#include <QFont>
42
#include <QFontDatabase>
43
#include <QtMultimediaWidgets/QVideoWidget>
44
#include <QtMultimedia/QMediaPlayer>
45
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
46
#   include <QtMultimedia/QMediaPlaylist>
47
#else
48
#   include <QAudioOutput>
49
#   include <QMediaMetaData>
50
#endif
51
#include <QListWidget>
52
#include <QLayout>
53
#include <QSizePolicy>
54
#include <QUrl>
55
#include <QThread>
56
#ifdef Q_OS_IOS
57
#include <QtSensors/QOrientationSensor>
58
#endif
59
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
60
#   include <QSound>
61
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
62
#   include <QtSensors/QOrientationSensor>
63
#   include <qpa/qplatformscreen.h>
64
#endif
65
#else
66
#   include <QPlainTextEdit>
67
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
68
#   include <QGeoPositionInfoSource>
69
#   include <QtSensors/QOrientationReading>
70
#   include <QPermissions>
71
#endif
72
#endif
73
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_ANDROID)
74
#include <QtAndroidExtras/QAndroidJniObject>
75
#include <QtAndroidExtras/QtAndroid>
76
#endif
77
#include <functional>
78
//#include <mutex>
79
#ifdef Q_OS_ANDROID
80
#include <android/log.h>
81
#endif
82
#include "tpagemanager.h"
83
#include "tqtmain.h"
84
#include "tconfig.h"
85
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
86
#include "tqtsettings.h"
87
#endif
88
#include "tqkeyboard.h"
89
#include "tqkeypad.h"
90
#include "tcolor.h"
91
#include "texcept.h"
92
#include "ttpinit.h"
93
#include "tqdownload.h"
94
#include "tqtphone.h"
95
#include "tqeditline.h"
96
#include "tqtinputline.h"
97
#include "tqmarquee.h"
98
#include "tqtwait.h"
99
#include "terror.h"
100
#include "tresources.h"
101
#include "tqscrollarea.h"
102
#include "tlock.h"
103
#ifdef Q_OS_IOS
104
#include "ios/QASettings.h"
105
#include "ios/tiosrotate.h"
106
#include "ios/tiosbattery.h"
107
#endif
108
#if TESTMODE == 1
109
#include "testmode.h"
110
#endif
111
 
112
#if __cplusplus < 201402L
113
#   error "This module requires at least C++14 standard!"
114
#else   // __cplusplus < 201402L
115
#   if __cplusplus < 201703L
116
#       include <experimental/filesystem>
117
namespace fs = std::experimental::filesystem;
118
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
119
#   else    // __cplusplus < 201703L
120
#       include <filesystem>
121
#       ifdef __ANDROID__
122
namespace fs = std::__fs::filesystem;
123
#       else    // __ANDROID__
124
namespace fs = std::filesystem;
125
#       endif   // __ANDROID__
126
#   endif   // __cplusplus < 201703L
127
#endif  // __cplusplus < 201402L
128
 
129
 
130
extern amx::TAmxNet *gAmxNet;                   //!< Pointer to the class running the thread which handles the communication with the controller.
131
extern bool _restart_;                          //!< If this is set to true then the whole program will start over.
132
extern TPageManager *gPageManager;              //!< The pointer to the global defined main class.
133
static bool isRunning = false;                  //!< TRUE = the pageManager was started.
134
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
135
static double gScale = 1.0;                     //!< Global variable holding the scale factor.
136
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.
137
std::atomic<double> mScaleFactorW{1.0};
138
std::atomic<double> mScaleFactorH{1.0};
139
int gScreenWidth{0};
140
int gScreenHeight{0};
141
bool isPortrait{false};
142
#endif
143
 
144
using std::bind;
145
using std::map;
146
using std::pair;
147
using std::string;
148
using std::vector;
149
 
150
static string _NO_OBJECT = "The global class TObject is not available!";
151
 
152
/**
153
 * @brief qtmain is used here as the entry point for the surface.
154
 *
155
 * The main entry function parses the command line parameters, if there were
156
 * any and sets the basic attributes. It creates the main window and starts the
157
 * application.
158
 *
159
 * @param argc      The number of command line arguments
160
 * @param argv      A pointer to a 2 dimensional array containing the command
161
 *                  line parameters.
162
 * @param pmanager  A pointer to the page manager class which is the main class
163
 *                  managing everything.
164
 *
165
 * @return If no errors occured it returns 0.
166
 */
167
int qtmain(int argc, char **argv, TPageManager *pmanager)
168
{
169
    DECL_TRACER("qtmain(int argc, char **argv, TPageManager *pmanager)");
170
 
171
    if (!pmanager)
172
    {
173
        MSG_ERROR("Fatal: No pointer to the page manager received!");
174
        return 1;
175
    }
176
 
177
    gPageManager = pmanager;
178
#ifdef __ANDROID__
179
    MSG_INFO("Android API version: " << __ANDROID_API__);
180
 
181
#if __ANDROID_API__ < 30
182
#warning "The Android API version is less than 30! Some functions may not work!"
183
#endif  // __ANDROID_API__
184
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
185
    QAndroidJniObject activity = QtAndroid::androidActivity();
186
    QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/HideToolbar", "hide", "(Landroid/app/Activity;Z)V", activity.object(), true);
187
#else
188
    QJniObject activity = QNativeInterface::QAndroidApplication::context();
189
    QJniObject::callStaticMethod<void>("org/qtproject/theosys/HideToolbar", "hide", "(Landroid/app/Activity;Z)V", activity.object(), true);
190
#endif  // QT5_LINUX
191
#endif  // __ANDROID__
192
 
193
#if defined(Q_OS_ANDROID)
194
    QApplication::setAttribute(Qt::AA_ForceRasterWidgets);
195
//    QApplication::setAttribute(Qt::AA_Use96Dpi);
196
//    QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
197
#endif
198
 
199
    QApplication app(argc, argv);
486 andreas 200
    app.setApplicationName(QString::fromStdString(TConfig::getProgName()));
201
    app.setApplicationDisplayName("TPanel");
202
    app.setApplicationVersion(VERSION_STRING());
446 andreas 203
    // Set the orientation
204
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
205
    QScreen *screen = QGuiApplication::primaryScreen();
206
 
207
    if (!screen)
208
    {
209
        MSG_ERROR("Couldn't determine the primary screen!")
210
        return 1;
211
    }
212
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
213
    if (pmanager->getSettings()->isPortrait())  // portrait?
214
    {
215
        MSG_INFO("Orientation set to portrait mode.");
216
        screen->setOrientationUpdateMask(Qt::PortraitOrientation);
217
    }
218
    else
219
    {
220
        MSG_INFO("Orientation set to landscape mode.");
221
        screen->setOrientationUpdateMask(Qt::LandscapeOrientation);
222
    }
223
#endif  // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
224
    double scale = 1.0;
225
    // Calculate the scale factor
226
    if (TConfig::getScale())
227
    {
228
        // Because we've no window here we can not know on which screen, if
229
        // there are more than one, the application will start. Because on a
230
        // mobile mostly no external screen is connected, we take always the
231
        // resolution of the first (built in) screen.
232
        // TODO: Find a way to get the screen the application will start and
233
        // take this screen to calculate the scale factor.
449 andreas 234
        QSize screenSize = screen->size();
446 andreas 235
        double width = 0.0;
236
        double height = 0.0;
449 andreas 237
        gScreenWidth = std::max(screenSize.width(), screenSize.height());
238
        gScreenHeight = std::min(screenSize.height(), screenSize.width());
446 andreas 239
 
449 andreas 240
        if (screenSize.width() > screenSize.height())
446 andreas 241
            isPortrait = false;
242
        else
243
            isPortrait = true;
244
 
245
        int minWidth = pmanager->getSettings()->getWidth();
246
        int minHeight = pmanager->getSettings()->getHeight();
247
 
248
        if (pmanager->getSettings()->isPortrait())  // portrait?
249
        {
250
            width = std::min(gScreenWidth, gScreenHeight);
251
            height = std::max(gScreenHeight, gScreenWidth);
252
        }
253
        else
254
        {
255
            width = std::max(gScreenWidth, gScreenHeight);
256
            height = std::min(gScreenHeight, gScreenWidth);
257
        }
258
 
259
        if (!TConfig::getToolbarSuppress() && TConfig::getToolbarForce())
260
            minWidth += 48;
261
 
262
        MSG_INFO("Dimension of AMX screen:" << minWidth << " x " << minHeight);
263
        MSG_INFO("Screen size: " << width << " x " << height);
264
        // The scale factor is always calculated in difference to the prefered
265
        // size of the original AMX panel.
266
        mScaleFactorW = width / (double)minWidth;
267
        mScaleFactorH = height / (double)minHeight;
268
        scale = std::min(mScaleFactorW, mScaleFactorH);
269
#ifdef __ANDROID__
449 andreas 270
        __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "INF    ##, ???????????? scale: %f (Screen: %1.0fx%1.0f, Page: %dx%d)", scale, width, height, minWidth, minHeight);
446 andreas 271
#endif
272
        gScale = scale;     // The calculated scale factor
273
        gFullWidth = width;
274
        MSG_INFO("Calculated scale factor: " << scale);
275
        // This preprocessor variable allows the scaling to be done by the Skia
276
        // library, which is used to draw everything. In comparison to Qt this
277
        // library is a bit slower and sometimes does not honor the aspect ratio
278
        // correct. But in case there is another framework than Qt in use, this
279
        // could be necessary.
280
#ifdef _SCALE_SKIA_
281
        if (scale != 0.0)
282
        {
283
            pmanager->setScaleFactor(scale);
284
            MSG_INFO("Scale factor: " << scale);
285
        }
286
 
287
        if (scaleW != 0.0)
288
            pmanager->setScaleFactorWidth(scaleW);
289
 
290
        if (scaleH != 0.0)
291
            pmanager->setScaleFactorHeight(scaleH);
292
#endif  // _SCALE_SKIA_
293
    }
294
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
295
    // Initialize the application
296
    pmanager->setDPI(QGuiApplication::primaryScreen()->logicalDotsPerInch());
297
    QCoreApplication::setOrganizationName(TConfig::getProgName().c_str());
298
    QCoreApplication::setApplicationName("TPanel");
299
    QCoreApplication::setApplicationVersion(VERSION_STRING());
300
    QCommandLineParser parser;
301
    parser.setApplicationDescription(QCoreApplication::applicationName());
302
    parser.addHelpOption();
303
    parser.addVersionOption();
304
    parser.process(app);
305
 
306
    MainWindow mainWin;
307
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
308
#   ifndef _SCALE_SKIA_
309
    if (TConfig::getScale() && scale != 1.0)
310
        mainWin.setScaleFactor(scale);
311
#   endif   // _SCALE_SKIA_
312
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
313
    mainWin.setConfigFile(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
314
    QPalette palette(mainWin.palette());
315
    palette.setColor(QPalette::Window, Qt::black);  // Keep the background black. Helps to save battery on OLED displays.
316
    mainWin.setPalette(palette);
317
    mainWin.grabGesture(Qt::PinchGesture);
318
    mainWin.grabGesture(Qt::SwipeGesture);
319
    mainWin.setOrientation(Qt::PrimaryOrientation);
320
//#ifdef Q_OS_IOS
321
 //   mainWin.setWindowFlag(Qt::MaximizeUsingFullscreenGeometryHint, true);
322
//#endif
323
 
324
    mainWin.show();
325
    return app.exec();
326
}
327
 
328
/**
329
 * @brief MainWindow::MainWindow constructor
330
 *
331
 * This method is the constructor for this class. It registers the callback
332
 * functions to the class TPageManager and starts the main run loop.
333
 *
334
 * Qt is used only to manage widgets to handle pages and subpages. A page as
335
 * well as a subpage may contain a background graphic and some elements. The
336
 * elements could be buttons, bargraphs and other objects. The underlying layer
337
 * draw every element as a ready graphic image and call a callback function to
338
 * let Qt display the graphic.
339
 * If there are some animations on a subpage defined, this is also handled by
340
 * Qt. Especialy sliding and fading.
341
 */
342
MainWindow::MainWindow()
461 andreas 343
    : mIntercom(this)
446 andreas 344
{
345
    DECL_TRACER("MainWindow::MainWindow()");
346
 
347
    TObject::setParent(this);
348
 
349
    if (!gPageManager)
350
    {
351
        EXCEPTFATALMSG("The class TPageManager was not initialized!");
352
    }
353
 
354
    mGestureFilter = new TQGestureFilter(this);
355
    connect(mGestureFilter, &TQGestureFilter::gestureEvent, this, &MainWindow::onGestureEvent);
356
    setAttribute(Qt::WA_AcceptTouchEvents, true);   // We accept touch events
357
    grabGesture(Qt::PinchGesture);                  // We use a pinch gesture to open the settings dialog
358
    grabGesture(Qt::SwipeGesture);                  // We support swiping also
359
 
360
#ifdef Q_OS_IOS                                     // Block autorotate on IOS
361
//    initGeoLocation();
362
    mIosRotate = new TIOSRotate;
363
//    mIosRotate->automaticRotation(false);           // FIXME: This doesn't work!
364
 
365
    if (!mSensor)
366
    {
367
        mSensor = new QOrientationSensor(this);
368
 
369
        if (mSensor)
370
        {
371
            mSensor->setAxesOrientationMode(QSensor::AutomaticOrientation);
372
 
373
            if (gPageManager && gPageManager->getSettings()->isPortrait())
374
                mSensor->setCurrentOrientation(Qt::PortraitOrientation);
375
            else
376
                mSensor->setCurrentOrientation(Qt::LandscapeOrientation);
377
        }
378
    }
379
#endif  // Q_OS_IOS
380
 
381
    // We create the central widget here to make sure the application
382
    // initializes correct. On mobiles the whole screen is used while on
383
    // desktops a window with the necessary size is created.
384
    QWidget *central = new QWidget;
385
    central->setObjectName("centralWidget");
386
    central->setBackgroundRole(QPalette::Window);
387
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
388
//    central->setAutoFillBackground(false);
389
    central->setFixedSize(gScreenWidth, gScreenHeight);
390
#endif  // defined(Q_OS_IOS) || defined(Q_OSANDROID)
391
    setCentralWidget(central);      // Here we set the central widget
392
    central->show();
393
    // This is a stacked widget used to hold all pages. With it we can also
394
    // simply manage the objects bound to a page.
395
    mCentralWidget = new QStackedWidget(central);
396
    mCentralWidget->setObjectName("stackedPageWidgets");
397
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
398
    MSG_DEBUG("Size will be set for " << (isPortrait ? "PORTRAIT" : "LANDSCAPE"));
399
 
400
    if (gPageManager && gPageManager->getSettings()->isLandscape())
401
    {
402
        if (!isPortrait)
403
            mCentralWidget->setFixedSize((mToolbar ? gScreenWidth - mToolbar->width() : gScreenWidth), gScreenHeight);
404
        else
405
            mCentralWidget->setFixedSize(gScreenWidth, gScreenHeight);
406
    }
407
    else
408
    {
409
        if (isPortrait)
410
            mCentralWidget->setFixedSize((mToolbar ? gScreenHeight - mToolbar->width() : gScreenHeight), gScreenWidth);
411
        else
412
            mCentralWidget->setFixedSize(gScreenHeight, gScreenWidth);
413
    }
414
#else
415
    QSize qs = menuBar()->sizeHint();
416
    QSize icSize =  iconSize();
417
    int lwidth = gPageManager->getSettings()->getWidth() + icSize.width() + 16;
418
    int lheight = gPageManager->getSettings()->getHeight() + qs.height();
419
    mCentralWidget->setFixedSize(gPageManager->getSettings()->getWidth(), gPageManager->getSettings()->getHeight());
420
#endif  // defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
421
 
422
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
423
    if (gPageManager->getSettings()->isPortrait())  // portrait?
424
    {
425
        MSG_INFO("Orientation set to portrait mode.");
426
        _setOrientation(O_PORTRAIT);
427
        mOrientation = Qt::PortraitOrientation;
428
    }
429
    else
430
    {
431
        MSG_INFO("Orientation set to landscape mode.");
432
        _setOrientation(O_LANDSCAPE);
433
        mOrientation = Qt::LandscapeOrientation;
434
    }
435
#else
436
    QRect rectMain = geometry();
437
    rectMain.setWidth(lwidth);
438
    rectMain.setHeight(lheight);
439
    setGeometry(rectMain);
440
    MSG_DEBUG("Height of main window:  " << rectMain.height());
441
    MSG_DEBUG("Height of panel screen: " << lheight);
442
    // If our first top pixel is not 0, maybe because of a menu, window
443
    // decorations or a toolbar, we must add this extra height to the
444
    // positions of widgets and mouse presses.
445
    int avHeight = rectMain.height() - gPageManager->getSettings()->getHeight();
446
    MSG_DEBUG("Difference in height:   " << avHeight);
447
    gPageManager->setFirstTopPixel(avHeight);
448
#endif
449
    setWindowIcon(QIcon(":images/icon.png"));
450
 
451
    // First we register all our surface callbacks to the underlying work
452
    // layer. All the graphics are drawn by the Skia library. The layer below
453
    // call the following functions to let Qt display the graphics on the
454
    // screen let it manage the widgets containing the graphics.
455
    gPageManager->registerCallbackDB(bind(&MainWindow::_displayButton, this,
456
                                       std::placeholders::_1,
457
                                       std::placeholders::_2,
458
                                       std::placeholders::_3,
459
                                       std::placeholders::_4,
460
                                       std::placeholders::_5,
461
                                       std::placeholders::_6,
462
                                       std::placeholders::_7,
463
                                       std::placeholders::_8,
464
                                       std::placeholders::_9,
465
                                       std::placeholders::_10));
466
 
467
    gPageManager->registerSetMarqueeText(bind(&MainWindow::_setMarqueeText, this, std::placeholders::_1));
468
 
469
    gPageManager->regDisplayViewButton(bind(&MainWindow::_displayViewButton, this,
470
                                          std::placeholders::_1,
471
                                          std::placeholders::_2,
472
                                          std::placeholders::_3,
473
                                          std::placeholders::_4,
474
                                          std::placeholders::_5,
475
                                          std::placeholders::_6,
476
                                          std::placeholders::_7,
477
                                          std::placeholders::_8,
478
                                          std::placeholders::_9,
479
                                          std::placeholders::_10));
480
 
481
    gPageManager->regAddViewButtonItems(bind(&MainWindow::_addViewButtonItems, this,
482
                                             std::placeholders::_1,
483
                                             std::placeholders::_2));
484
 
485
    gPageManager->regUpdateViewButton(bind(&MainWindow::_updateViewButton, this,
486
                                           std::placeholders::_1,
487
                                           std::placeholders::_2,
488
                                           std::placeholders::_3,
489
                                           std::placeholders::_4));
490
 
491
    gPageManager->regUpdateViewButtonItem(bind(&MainWindow::_updateViewButtonItem, this,
492
                                               std::placeholders::_1,
493
                                               std::placeholders::_2));
494
 
495
    gPageManager->regShowSubViewItem(bind(&MainWindow::_showViewButtonItem, this,
496
                                          std::placeholders::_1,
497
                                          std::placeholders::_2,
498
                                          std::placeholders::_3,
499
                                          std::placeholders::_4));
500
 
501
    gPageManager->regHideAllSubViewItems(bind(&MainWindow::_hideAllViewItems, this, std::placeholders::_1));
502
    gPageManager->regHideSubViewItem(bind(&MainWindow::_hideViewItem, this, std::placeholders::_1, std::placeholders::_2));
503
    gPageManager->regSetSubViewPadding(bind(&MainWindow::_setSubViewPadding, this, std::placeholders::_1, std::placeholders::_2));
504
 
505
    gPageManager->registerCallbackSP(bind(&MainWindow::_setPage, this,
506
                                         std::placeholders::_1,
507
                                         std::placeholders::_2,
508
                                         std::placeholders::_3));
509
 
510
    gPageManager->registerCallbackSSP(bind(&MainWindow::_setSubPage, this,
511
                                          std::placeholders::_1,
512
                                          std::placeholders::_2,
513
                                          std::placeholders::_3,
514
                                          std::placeholders::_4,
515
                                          std::placeholders::_5,
516
                                          std::placeholders::_6,
517
                                          std::placeholders::_7,
518
                                          std::placeholders::_8));
519
 
520
    gPageManager->registerCallbackSB(bind(&MainWindow::_setBackground, this,
521
                                         std::placeholders::_1,
522
                                         std::placeholders::_2,
523
                                         std::placeholders::_3,
524
                                         std::placeholders::_4,
525
#ifdef _OPAQUE_SKIA_
526
                                         std::placeholders::_5));
527
#else
528
                                         std::placeholders::_5,
529
                                         std::placeholders::_6));
530
#endif
531
    gPageManager->regCallDropPage(bind(&MainWindow::_dropPage, this, std::placeholders::_1));
532
    gPageManager->regCallDropSubPage(bind(&MainWindow::_dropSubPage, this, std::placeholders::_1, std::placeholders::_2));
533
    gPageManager->regCallPlayVideo(bind(&MainWindow::_playVideo, this,
534
                                       std::placeholders::_1,
535
                                       std::placeholders::_2,
536
                                       std::placeholders::_3,
537
                                       std::placeholders::_4,
538
                                       std::placeholders::_5,
539
                                       std::placeholders::_6,
540
                                       std::placeholders::_7,
541
                                       std::placeholders::_8,
542
                                       std::placeholders::_9));
543
    gPageManager->regCallInputText(bind(&MainWindow::_inputText, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
544
    gPageManager->regCallListBox(bind(&MainWindow::_listBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
545
    gPageManager->registerDropButton(bind(&MainWindow::_dropButton, this, std::placeholders::_1));
546
    gPageManager->regCallbackKeyboard(bind(&MainWindow::_showKeyboard, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
547
    gPageManager->regCallbackKeypad(bind(&MainWindow::_showKeypad, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
548
    gPageManager->regCallResetKeyboard(bind(&MainWindow::_resetKeyboard, this));
549
    gPageManager->regCallShowSetup(bind(&MainWindow::_showSetup, this));
550
    gPageManager->regCallbackResetSurface(bind(&MainWindow::_resetSurface, this));
551
    gPageManager->regCallbackShutdown(bind(&MainWindow::_shutdown, this));
552
    gPageManager->regCallbackPlaySound(bind(&MainWindow::_playSound, this, std::placeholders::_1));
553
    gPageManager->regCallbackStopSound(bind(&MainWindow::_stopSound, this));
554
    gPageManager->regCallbackMuteSound(bind(&MainWindow::_muteSound, this, std::placeholders::_1));
555
    gPageManager->regCallbackSetVolume(bind(&MainWindow::_setVolume, this, std::placeholders::_1));
556
    gPageManager->registerCBsetVisible(bind(&MainWindow::_setVisible, this, std::placeholders::_1, std::placeholders::_2));
557
    gPageManager->regSendVirtualKeys(bind(&MainWindow::_sendVirtualKeys, this, std::placeholders::_1));
558
    gPageManager->regShowPhoneDialog(bind(&MainWindow::_showPhoneDialog, this, std::placeholders::_1));
559
    gPageManager->regSetPhoneNumber(bind(&MainWindow::_setPhoneNumber, this, std::placeholders::_1));
560
    gPageManager->regSetPhoneStatus(bind(&MainWindow::_setPhoneStatus, this, std::placeholders::_1));
561
    gPageManager->regSetPhoneState(bind(&MainWindow::_setPhoneState, this, std::placeholders::_1, std::placeholders::_2));
562
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
563
    gPageManager->regOnOrientationChange(bind(&MainWindow::_orientationChanged, this, std::placeholders::_1));
564
    gPageManager->regOnSettingsChanged(bind(&MainWindow::_activateSettings, this, std::placeholders::_1,
565
                                            std::placeholders::_2,
566
                                            std::placeholders::_3,
567
                                            std::placeholders::_4,
568
                                            std::placeholders::_5,
569
                                            std::placeholders::_6));
570
#endif
571
    gPageManager->regRepaintWindows(bind(&MainWindow::_repaintWindows, this));
572
    gPageManager->regToFront(bind(&MainWindow::_toFront, this, std::placeholders::_1));
573
#if !defined (Q_OS_ANDROID) && !defined(Q_OS_IOS)
574
    gPageManager->regSetMainWindowSize(bind(&MainWindow::_setSizeMainWindow, this, std::placeholders::_1, std::placeholders::_2));
575
#endif
576
    gPageManager->regDownloadSurface(bind(&MainWindow::_downloadSurface, this, std::placeholders::_1, std::placeholders::_2));
577
    gPageManager->regDisplayMessage(bind(&MainWindow::_displayMessage, this, std::placeholders::_1, std::placeholders::_2));
578
    gPageManager->regAskPassword(bind(&MainWindow::_askPassword, this, std::placeholders::_1,
579
                                      std::placeholders::_2,
580
                                      std::placeholders::_3,
581
                                      std::placeholders::_4,
582
                                      std::placeholders::_5));
583
    gPageManager->regFileDialogFunction(bind(&MainWindow::_fileDialog, this,
584
                                             std::placeholders::_1,
585
                                             std::placeholders::_2,
586
                                             std::placeholders::_3,
587
                                             std::placeholders::_4));
588
    gPageManager->regStartWait(bind(&MainWindow::_startWait, this, std::placeholders::_1));
589
    gPageManager->regStopWait(bind(&MainWindow::_stopWait, this));
590
    gPageManager->regPageFinished(bind(&MainWindow::_pageFinished, this, std::placeholders::_1));
458 andreas 591
    gPageManager->regInitializeIntercom(bind(&MainWindow::_initializeIntercom, this, std::placeholders::_1));
592
    gPageManager->regIntercomStart(bind(&MainWindow::_intercomStart, this));
593
    gPageManager->regIntercomStop(bind(&MainWindow::_intercomStop, this));
594
    gPageManager->regIntercomSpkLevel(bind(&MainWindow::_intercomSpkLevel, this, std::placeholders::_1));
595
    gPageManager->regIntercomMicLevel(bind(&MainWindow::_intercomMicLevel, this, std::placeholders::_1));
596
    gPageManager->regIntercomMute(bind(&MainWindow::_intercomMicMute, this, std::placeholders::_1));
446 andreas 597
    gPageManager->deployCallbacks();
598
 
599
    createActions();        // Create the toolbar, if enabled by settings.
600
 
601
#ifndef QT_NO_SESSIONMANAGER
602
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
603
    QGuiApplication::setFallbackSessionManagementEnabled(false);
604
    connect(qApp, &QGuiApplication::commitDataRequest,
605
            this, &MainWindow::writeSettings);
606
#endif  // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
607
#endif  // QT_NO_SESSIONMANAGER
608
 
609
    // Some types used to transport data from the layer below.
610
    qRegisterMetaType<size_t>("size_t");
611
    qRegisterMetaType<QByteArray>("QByteArray");
612
    qRegisterMetaType<ANIMATION_t>("ANIMATION_t");
613
    qRegisterMetaType<Button::TButton*>("Button::TButton*");
614
    qRegisterMetaType<std::string>("std::string");
615
    qRegisterMetaType<SUBVIEWLIST_T>("SUBVIEWLIST_T");
616
    qRegisterMetaType<PGSUBVIEWITEM_T>("PGSUBVIEWITEM_T");
617
    qRegisterMetaType<PGSUBVIEWITEM_T>("PGSUBVIEWITEM_T&");
618
    qRegisterMetaType<PGSUBVIEWATOM_T>("PGSUBVIEWATOM_T");
619
    qRegisterMetaType<TBitmap>("TBitmap");
458 andreas 620
    qRegisterMetaType<INTERCOM_t>("INTERCOM_t");
446 andreas 621
 
622
    // All the callback functions doesn't act directly. Instead they emit an
623
    // event. Then Qt decides whether the real function is started directly and
624
    // immediately or if the call is queued and called later in a thread. To
625
    // handle this we're "connecting" the real functions to some signals.
626
    try
627
    {
628
        connect(this, &MainWindow::sigDisplayButton, this, &MainWindow::displayButton);
629
        connect(this, &MainWindow::sigSetMarqueeText, this, &MainWindow::setMarqueeText);
630
        connect(this, &MainWindow::sigDisplayViewButton, this, &MainWindow::displayViewButton);
631
        connect(this, &MainWindow::sigAddViewButtonItems, this, &MainWindow::addViewButtonItems);
632
        connect(this, &MainWindow::sigShowViewButtonItem, this, &MainWindow::showViewButtonItem);
633
        connect(this, &MainWindow::sigUpdateViewButton, this, &MainWindow::updateViewButton);
634
        connect(this, &MainWindow::sigUpdateViewButtonItem, this, &MainWindow::updateViewButtonItem);
635
        connect(this, &MainWindow::sigSetPage, this, &MainWindow::setPage);
636
        connect(this, &MainWindow::sigSetSubPage, this, &MainWindow::setSubPage);
637
        connect(this, &MainWindow::sigSetBackground, this, &MainWindow::setBackground);
638
        connect(this, &MainWindow::sigDropPage, this, &MainWindow::dropPage);
639
        connect(this, &MainWindow::sigDropSubPage, this, &MainWindow::dropSubPage);
640
        connect(this, &MainWindow::sigPlayVideo, this, &MainWindow::playVideo);
641
        connect(this, &MainWindow::sigInputText, this, &MainWindow::inputText);
642
        connect(this, &MainWindow::sigListBox, this, &MainWindow::listBox);
643
        connect(this, &MainWindow::sigKeyboard, this, &MainWindow::showKeyboard);
644
        connect(this, &MainWindow::sigKeypad, this, &MainWindow::showKeypad);
645
        connect(this, &MainWindow::sigShowSetup, this, &MainWindow::showSetup);
646
        connect(this, &MainWindow::sigPlaySound, this, &MainWindow::playSound);
647
        connect(this, &MainWindow::sigSetVolume, this, &MainWindow::setVolume);
648
        connect(this, &MainWindow::sigDropButton, this, &MainWindow::dropButton);
649
        connect(this, &MainWindow::sigSetVisible, this, &MainWindow::SetVisible);
650
        connect(this, &MainWindow::sigSendVirtualKeys, this, &MainWindow::sendVirtualKeys);
651
        connect(this, &MainWindow::sigShowPhoneDialog, this, &MainWindow::showPhoneDialog);
652
        connect(this, &MainWindow::sigSetPhoneNumber, this, &MainWindow::setPhoneNumber);
653
        connect(this, &MainWindow::sigSetPhoneStatus, this, &MainWindow::setPhoneStatus);
654
        connect(this, &MainWindow::sigSetPhoneState, this, &MainWindow::setPhoneState);
655
        connect(this, &MainWindow::sigRepaintWindows, this, &MainWindow::repaintWindows);
656
        connect(this, &MainWindow::sigToFront, this, &MainWindow::toFront);
657
        connect(this, &MainWindow::sigOnProgressChanged, this, &MainWindow::onProgressChanged);
658
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
659
        connect(this, &MainWindow::sigSetSizeMainWindow, this, &MainWindow::setSizeMainWindow);
660
#endif
661
        connect(this, &MainWindow::sigDownloadSurface, this, &MainWindow::downloadSurface);
662
        connect(this, &MainWindow::sigDisplayMessage, this, &MainWindow::displayMessage);
663
        connect(this, &MainWindow::sigAskPassword, this, &MainWindow::askPassword);
664
        connect(this, &MainWindow::sigFileDialog, this, &MainWindow::fileDialog);
665
        connect(this, &MainWindow::sigStartWait, this, &MainWindow::startWait);
666
        connect(this, &MainWindow::sigStopWait, this, &MainWindow::stopWait);
667
        connect(this, &MainWindow::sigPageFinished, this, &MainWindow::pageFinished);
465 andreas 668
        connect(this, &MainWindow::sigInitializeIntercom, this, &MainWindow::initializeIntercom);
669
        connect(this, &MainWindow::sigIntercomStart, this, &MainWindow::intercomStart);
670
        connect(this, &MainWindow::sigIntercomStop, this, &MainWindow::intercomStop);
671
        connect(this, &MainWindow::sigIntercomSpkLevel, this, &MainWindow::intercomSpkLevel);
672
        connect(this, &MainWindow::sigIintercomMicLevel, this, &MainWindow::intercomMicLevel);
673
        connect(this, &MainWindow::sigIntercomMicMute, this, &MainWindow::intercomMicMute);
446 andreas 674
        connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onAppStateChanged);
675
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
676
        QScreen *screen = QGuiApplication::primaryScreen();
677
        connect(screen, &QScreen::orientationChanged, this, &MainWindow::onScreenOrientationChanged);
678
        connect(this, &MainWindow::sigActivateSettings, this, &MainWindow::activateSettings);
679
#endif
680
    }
681
    catch (std::exception& e)
682
    {
683
        MSG_ERROR("Connection error: " << e.what());
684
    }
685
    catch(...)
686
    {
687
        MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
688
    }
689
 
690
    setUnifiedTitleAndToolBarOnMac(true);
691
#ifdef Q_OS_ANDROID
692
    // At least initialize the phone call listener
693
    if (gPageManager)
694
        gPageManager->initPhoneState();
695
 
696
    /*
697
     * In case this class was created not the first time, we initiate a small
698
     * thread to send the signal ApplicationActive to initiate the communication
699
     * with the controller again. This also starts the page manager thread
700
     * (TPageManager), which handles all elements of the surface.
701
     */
702
    if (_restart_ && gPageManager)
703
    {
704
        try
705
        {
706
            std::thread mThread = std::thread([=] { this->_signalState(Qt::ApplicationActive); });
707
            mThread.detach();   // We detach immediately to leave this method.
708
        }
709
        catch (std::exception& e)
710
        {
711
            MSG_ERROR("Error starting the thread to reinvoke communication!");
712
        }
713
        catch(...)
714
        {
715
            MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
716
        }
717
    }
718
#endif
719
 
720
#ifdef Q_OS_IOS
721
    // To get the battery level periodicaly we setup a timer.
722
    if (!mIosBattery)
723
        mIosBattery = new TIOSBattery;
724
 
725
    mIosBattery->update();
726
 
727
    int left = mIosBattery->getBatteryLeft();
728
    int state = mIosBattery->getBatteryState();
729
    // At this point no buttons are registered and therefore the battery
730
    // state will not be visible. To have the state at the moment a button
731
    // is registered, we tell the page manager to store the values.
732
    gPageManager->setBattery(left, state);
733
    MSG_DEBUG("Battery state was set to " << left << "% and state " << state);
734
#endif  // Q_OS_IOS
735
 
736
    _restart_ = false;
737
}
738
 
739
MainWindow::~MainWindow()
740
{
741
    DECL_TRACER("MainWindow::~MainWindow()");
742
 
743
    killed = true;
744
    prg_stopped = true;
745
 
746
    disconnect(this, &MainWindow::sigDisplayButton, this, &MainWindow::displayButton);
747
    disconnect(this, &MainWindow::sigDisplayViewButton, this, &MainWindow::displayViewButton);
748
    disconnect(this, &MainWindow::sigAddViewButtonItems, this, &MainWindow::addViewButtonItems);
749
    disconnect(this, &MainWindow::sigSetPage, this, &MainWindow::setPage);
750
    disconnect(this, &MainWindow::sigSetSubPage, this, &MainWindow::setSubPage);
751
    disconnect(this, &MainWindow::sigSetBackground, this, &MainWindow::setBackground);
752
    disconnect(this, &MainWindow::sigDropPage, this, &MainWindow::dropPage);
753
    disconnect(this, &MainWindow::sigDropSubPage, this, &MainWindow::dropSubPage);
754
    disconnect(this, &MainWindow::sigPlayVideo, this, &MainWindow::playVideo);
755
    disconnect(this, &MainWindow::sigInputText, this, &MainWindow::inputText);
756
    disconnect(this, &MainWindow::sigListBox, this, &MainWindow::listBox);
757
    disconnect(this, &MainWindow::sigKeyboard, this, &MainWindow::showKeyboard);
758
    disconnect(this, &MainWindow::sigKeypad, this, &MainWindow::showKeypad);
759
    disconnect(this, &MainWindow::sigShowSetup, this, &MainWindow::showSetup);
760
    disconnect(this, &MainWindow::sigPlaySound, this, &MainWindow::playSound);
761
    disconnect(this, &MainWindow::sigDropButton, this, &MainWindow::dropButton);
762
    disconnect(this, &MainWindow::sigSetVisible, this, &MainWindow::SetVisible);
763
    disconnect(this, &MainWindow::sigSendVirtualKeys, this, &MainWindow::sendVirtualKeys);
764
    disconnect(this, &MainWindow::sigShowPhoneDialog, this, &MainWindow::showPhoneDialog);
765
    disconnect(this, &MainWindow::sigSetPhoneNumber, this, &MainWindow::setPhoneNumber);
766
    disconnect(this, &MainWindow::sigSetPhoneStatus, this, &MainWindow::setPhoneStatus);
767
    disconnect(this, &MainWindow::sigSetPhoneState, this, &MainWindow::setPhoneState);
768
    disconnect(this, &MainWindow::sigRepaintWindows, this, &MainWindow::repaintWindows);
769
    disconnect(this, &MainWindow::sigToFront, this, &MainWindow::toFront);
770
    disconnect(this, &MainWindow::sigOnProgressChanged, this, &MainWindow::onProgressChanged);
771
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
772
    disconnect(this, &MainWindow::sigSetSizeMainWindow, this, &MainWindow::setSizeMainWindow);
773
#endif
774
    disconnect(this, &MainWindow::sigDownloadSurface, this, &MainWindow::downloadSurface);
775
    disconnect(this, &MainWindow::sigDisplayMessage, this, &MainWindow::displayMessage);
776
    disconnect(this, &MainWindow::sigFileDialog, this, &MainWindow::fileDialog);
777
    disconnect(this, &MainWindow::sigStartWait, this, &MainWindow::startWait);
778
    disconnect(this, &MainWindow::sigStopWait, this, &MainWindow::stopWait);
779
    disconnect(this, &MainWindow::sigPageFinished, this, &MainWindow::pageFinished);
780
    disconnect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onAppStateChanged);
781
 
782
#ifdef Q_OS_IOS
783
    if (mSource)
784
    {
785
        delete mSource;
786
        mSource = nullptr;
787
    }
788
#endif
789
    if (mMediaPlayer)
790
    {
791
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
792
        delete mAudioOutput;
793
#endif
794
        delete mMediaPlayer;
795
    }
796
 
797
    if (gAmxNet && !gAmxNet->isStopped())
798
        gAmxNet->stop();
799
 
800
    if (mToolbar)
801
    {
802
        removeToolBar(mToolbar);
803
        mToolbar = nullptr;
804
    }
805
 
806
    isRunning = false;
807
#ifdef Q_OS_IOS
808
    if (mIosRotate)
809
        mIosRotate->automaticRotation(true);
810
 
811
    if (mIosBattery)
812
    {
813
        delete mIosBattery;
814
        mIosBattery = nullptr;
815
    }
816
 
817
    if (mIosRotate)
818
        delete mIosRotate;
819
#endif
820
 
821
    if (mGestureFilter)
822
    {
823
        disconnect(mGestureFilter, &TQGestureFilter::gestureEvent, this, &MainWindow::onGestureEvent);
824
        delete mGestureFilter;
825
    }
826
}
827
 
828
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
829
/**
830
 * @brief Small thread to invoke the initialization on an Android device.
831
 *
832
 * On Android devices the signal ApplicationActive is not send if the class
833
 * \p MainWindow is destroyed and recreated. Therefore we need this little
834
 * helper to send the signal when the class is initialized.
835
 *
836
 * @param state     This defines the signal to send.
837
 */
838
void MainWindow::_signalState(Qt::ApplicationState state)
839
{
840
    DECL_TRACER("MainWindow::_signalState(Qt::ApplicationState state)");
841
 
842
    std::this_thread::sleep_for(std::chrono::seconds(1));   // Wait a second
843
    onAppStateChanged(state);
844
}
845
 
846
void MainWindow::_orientationChanged(int orientation)
847
{
848
    DECL_TRACER("MainWindow::_orientationChanged(int orientation)");
849
 
850
    if (gPageManager && gPageManager->getSettings()->isPortrait())  // portrait?
851
    {
852
        if (orientation == O_REVERSE_PORTRAIT && mOrientation != Qt::InvertedPortraitOrientation)
853
        {
854
            _setOrientation((J_ORIENTATION)orientation);
855
            mOrientation = Qt::InvertedPortraitOrientation;
856
        }
857
        else if (orientation == O_PORTRAIT && mOrientation != Qt::PortraitOrientation)
858
        {
859
            _setOrientation((J_ORIENTATION)orientation);
860
            mOrientation = Qt::PortraitOrientation;
861
        }
862
    }
863
    else
864
    {
865
        if (orientation == O_REVERSE_LANDSCAPE && mOrientation != Qt::InvertedLandscapeOrientation)
866
        {
867
            _setOrientation((J_ORIENTATION)orientation);
868
            mOrientation = Qt::InvertedLandscapeOrientation;
869
        }
870
        else if (orientation == O_LANDSCAPE && mOrientation != Qt::LandscapeOrientation)
871
        {
872
            _setOrientation((J_ORIENTATION)orientation);
873
            mOrientation = Qt::LandscapeOrientation;
874
        }
875
    }
876
}
877
 
878
void MainWindow::_activateSettings(const std::string& oldNetlinx, int oldPort, int oldChannelID, const std::string& oldSurface, bool oldToolbarSuppress, bool oldToolbarForce)
879
{
880
    DECL_TRACER("MainWindow::_activateSettings(const std::string& oldNetlinx, int oldPort, int oldChannelID, const std::string& oldSurface, bool oldToolbarSuppress, bool oldToolbarForce)");
881
 
882
    if (!mHasFocus)
883
        return;
884
 
885
    emit sigActivateSettings(oldNetlinx, oldPort, oldChannelID, oldSurface, oldToolbarSuppress, oldToolbarForce);
886
}
887
 
888
/**
889
 * @brief MainWindow::activateSettings
890
 * This method activates some urgent settings. It is called on Android and IOS
891
 * after the setup dialog was closed. The method expects some values taken
892
 * immediately before the setup dialog was started. If takes some actions like
893
 * downloading a surface when the setting for it changed or removes the
894
 * toolbar on the right if the user reuqsted it.
895
 *
896
 * @param oldNetlinx        The IP or name of the Netlinx
897
 * @param oldPort           The network port number used to connect to Netlinx
898
 * @param oldChannelID      The channel ID TPanel uses to identify against the
899
 *                          Netlinx.
900
 * @param oldSurface        The name of theprevious TP4 file.
901
 * @param oldToolbarSuppress    State of toolbar suppress switch
902
 * @param oldToolbarForce   The state of toolbar force switch
903
 */
904
void MainWindow::activateSettings(const std::string& oldNetlinx, int oldPort, int oldChannelID, const std::string& oldSurface, bool oldToolbarSuppress, bool oldToolbarForce)
905
{
906
    DECL_TRACER("MainWindow::activateSettings(const std::string& oldNetlinx, int oldPort, int oldChannelID, const std::string& oldSurface, bool oldToolbarSuppress, bool oldToolbarForce)");
907
 
908
#ifdef Q_OS_IOS
909
    TConfig cf(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
910
#endif
911
    bool rebootAnyway = false;
912
    bool doDownload = false;
913
    string newSurface = TConfig::getFtpSurface();
914
 
915
    if (!TConfig::getToolbarSuppress() && oldToolbarForce != TConfig::getToolbarForce())
916
    {
917
        QMessageBox msgBox(this);
918
        msgBox.setText("The change for the visibility of the toolbar will be active on the next start of TPanel!");
919
        msgBox.exec();
920
    }
921
    else if (oldToolbarSuppress != TConfig::getToolbarSuppress() && TConfig::getToolbarSuppress())
922
    {
923
        if (mToolbar)
924
        {
925
            mToolbar->close();
926
            delete mToolbar;
927
            mToolbar = nullptr;
928
        }
929
    }
930
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
931
    if (newSurface != oldSurface || TTPInit::haveSystemMarker())
932
#else
933
    time_t dlTime = TConfig::getFtpDownloadTime();
934
    time_t actTime = time(NULL);
935
 
936
    if (newSurface != oldSurface || dlTime == 0 || dlTime < (actTime - 60))
937
#endif
938
    {
939
        MSG_DEBUG("Surface should be downloaded (Old: " << oldSurface << ", New: " << newSurface << ")");
940
 
941
        QMessageBox msgBox(this);
942
        msgBox.setText(QString("Should the surface <b>") + newSurface.c_str() + "</b> be installed?");
943
        msgBox.addButton(QMessageBox::Yes);
944
        msgBox.addButton(QMessageBox::No);
945
        int ret = msgBox.exec();
946
 
947
        if (ret == QMessageBox::Yes)
948
        {
949
            doDownload = true;
950
            TTPInit tpinit;
951
            std::vector<TTPInit::FILELIST_t> mFileList;
952
            // Get the list of TP4 files from NetLinx, if there are any.
953
            TQtWait waitBox(this, string("Please wait while I'm looking at the disc of Netlinx (") + TConfig::getController().c_str() + ") for TP4 files ...");
954
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
955
            waitBox.setScaleFactor(mScaleFactor);
956
            waitBox.doResize();
957
            waitBox.start();
958
#endif
959
            tpinit.setPath(TConfig::getProjectPath());
960
            tpinit.regCallbackProcessEvents(bind(&MainWindow::runEvents, this));
961
            tpinit.regCallbackProgressBar(bind(&MainWindow::_onProgressChanged, this, std::placeholders::_1));
962
            mFileList = tpinit.getFileList(".tp4");
963
            bool found = false;
964
 
965
            if (mFileList.size() > 0)
966
            {
967
                vector<TTPInit::FILELIST_t>::iterator iter;
968
 
969
                for (iter = mFileList.begin(); iter != mFileList.end(); ++iter)
970
                {
971
                    if (iter->fname == newSurface)
972
                    {
973
                        tpinit.setFileSize(iter->size);
974
                        found = true;
975
                        break;
976
                    }
977
                }
978
            }
979
 
980
            waitBox.end();
981
 
982
            if (found)
983
            {
984
                string msg = "Loading file <b>" + newSurface + "</b>.";
985
                MSG_DEBUG("Download of surface " << newSurface << " was forced!");
986
 
987
                downloadBar(msg, this);
988
 
989
                if (tpinit.loadSurfaceFromController(true))
990
                    rebootAnyway = true;
991
 
992
                mDownloadBar->close();
993
                mBusy = false;
994
            }
995
            else
996
            {
997
                MSG_PROTOCOL("The surface " << newSurface << " does not exist on NetLinx or the NetLinx " << TConfig::getController() << " was not found!");
998
                displayMessage("The surface " + newSurface + " does not exist on NetLinx or the NetLinx " + TConfig::getController() + " was not found!", "Information");
999
            }
1000
        }
1001
    }
1002
 
1003
    if (doDownload &&
1004
        (TConfig::getController() != oldNetlinx ||
1005
        TConfig::getChannel() != oldChannelID ||
1006
        TConfig::getPort() != oldPort || rebootAnyway))
1007
    {
1008
        // Start over by exiting this class
1009
        MSG_INFO("Program will start over!");
1010
        _restart_ = true;
1011
        prg_stopped = true;
1012
        killed = true;
1013
 
1014
        if (gAmxNet)
1015
            gAmxNet->stop();
1016
 
1017
        close();
1018
    }
1019
#ifdef Q_OS_ANDROID
1020
    else
1021
    {
1022
        TConfig cf(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
1023
    }
1024
#endif
1025
}
1026
 
1027
/**
1028
 * @brief MainWindow::_freezeWorkaround: A workaround for the screen freeze.
1029
 * On Mobiles the screen sometimes stay frozen after the application state
1030
 * changes to ACTIVE or some yet unidentified things happened.
1031
 * The workaround produces a faked geometry change which makes the Qt framework
1032
 * to reattach to the screen.
1033
 * There may be situations where this workaround could trigger a repaint of all
1034
 * objects on the screen but then the surface is still frozen. At the moment of
1035
 * writing this comment I have no workaround or even an explanation for this.
1036
 */
1037
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1038
/**
1039
 * @brief MainWindow::_freezeWorkaround
1040
 * This hack was made from Thomas Andersen. You'll find it at:
1041
 * https://bugreports.qt.io/browse/QTBUG-76142
1042
 */
1043
void MainWindow::_freezeWorkaround()
1044
{
1045
    DECL_TRACER("MainWindow::_freezeWorkaround()");
1046
 
1047
    QScreen* scr = QGuiApplication::screens().first();
1048
    QPlatformScreen* l_pScr = scr->handle(); /*QAndroidPlatformScreen*/
1049
    QRect l_geomHackAdjustedRect = l_pScr->availableGeometry();
1050
    QRect l_geomHackRect = l_geomHackAdjustedRect;
1051
    l_geomHackAdjustedRect.adjust(0, 0, 0, 5);
1052
    QMetaObject::invokeMethod(dynamic_cast<QObject*>(l_pScr), "setAvailableGeometry", Qt::DirectConnection, Q_ARG( const QRect &, l_geomHackAdjustedRect ));
1053
    QMetaObject::invokeMethod(dynamic_cast<QObject*>(l_pScr), "setAvailableGeometry", Qt::QueuedConnection, Q_ARG( const QRect &, l_geomHackRect ));
1054
}
1055
#endif  // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1056
#endif  // Q_OS_ANDROID || Q_OS_IOS
1057
 
1058
void MainWindow::_repaintWindows()
1059
{
1060
    DECL_TRACER("MainWindow::_repaintWindows()");
1061
 
1062
    if (mHasFocus)
1063
        emit sigRepaintWindows();
1064
}
1065
 
1066
void MainWindow::_toFront(ulong handle)
1067
{
1068
    DECL_TRACER("MainWindow::_toFront(ulong handle)");
1069
 
1070
    if (mHasFocus)
1071
        emit sigToFront(handle);
1072
}
1073
 
1074
void MainWindow::_downloadSurface(const string &file, size_t size)
1075
{
1076
    DECL_TRACER("MainWindow::_downloadSurface(const string &file, size_t size)");
1077
 
1078
    if (mHasFocus)
1079
        emit sigDownloadSurface(file, size);
1080
}
1081
 
1082
void MainWindow::_startWait(const string& text)
1083
{
1084
    DECL_TRACER("MainWindow::_startWait(const string& text)");
1085
 
1086
    emit sigStartWait(text);
1087
}
1088
 
1089
void MainWindow::_stopWait()
1090
{
1091
    DECL_TRACER("MainWindow::_stopWait()");
1092
 
1093
    emit sigStopWait();
1094
}
1095
 
1096
void MainWindow::_pageFinished(uint handle)
1097
{
1098
    DECL_TRACER("MainWindow::_pageFinished(uint handle)");
1099
 
1100
    emit sigPageFinished(handle);
1101
}
1102
 
1103
/**
1104
 * @brief MainWindow::closeEvent called when the application receives an exit event.
1105
 *
1106
 * If the user clicks on the exit icon or on the menu point _Exit_ this method
1107
 * is called. It makes sure everything is written to the configuration file
1108
 * and accepts the evernt.
1109
 *
1110
 * @param event The exit event
1111
 */
1112
void MainWindow::closeEvent(QCloseEvent *event)
1113
{
1114
    DECL_TRACER("MainWindow::closeEvent(QCloseEvent *event)");
1115
#ifdef __ANDROID__
1116
    __android_log_print(ANDROID_LOG_DEBUG,"tpanel","Close event; settingsChanged=%s", (settingsChanged ? "true":"false"));
1117
#endif
1118
    if (settingsChanged)
1119
    {
1120
        writeSettings();
1121
        event->accept();
1122
    }
1123
    else
1124
    {
1125
        prg_stopped = true;
1126
        killed = true;
1127
        MSG_INFO("Program will stop!");
1128
#ifdef __ANDROID__
1129
        if (gPageManager)
1130
            gPageManager->stopNetworkState();
1131
#endif
1132
        event->accept();
1133
    }
1134
}
1135
 
1136
/**
1137
 * @brief MainWindow::event Looks for a gesture
1138
 * @param event The event occured
1139
 * @return TRUE if event was accepted
1140
 */
1141
bool MainWindow::event(QEvent* event)
1142
{
1143
    if (event->type() == QEvent::Gesture)
1144
        return gestureEvent(static_cast<QGestureEvent*>(event));
1145
 
1146
    return QMainWindow::event(event);
1147
}
1148
 
1149
/**
1150
 * @brief MainWindow::gestureEvent handles a pinch event
1151
 * If a pinch event occured where the scale factor increased, the settings
1152
 * dialog is called. This exists for devices, where the left toolbox is not
1153
 * visible.
1154
 * @param event The guesture occured
1155
 * @return FALSE
1156
 */
1157
bool MainWindow::gestureEvent(QGestureEvent* event)
1158
{
1159
    DECL_TRACER("MainWindow::gestureEvent(QGestureEvent* event)");
1160
 
1161
    if (QGesture *pinch = event->gesture(Qt::PinchGesture))
1162
    {
1163
        QPinchGesture *pg = static_cast<QPinchGesture *>(pinch);
1164
#ifdef QT_DEBUG
1165
        string gs;
1166
 
1167
        switch(pg->state())
1168
        {
1169
            case Qt::NoGesture:         gs.assign("no gesture"); break;
1170
            case Qt::GestureStarted:    gs.assign("gesture started"); break;
1171
            case Qt::GestureUpdated:    gs.assign("gesture updated"); break;
1172
            case Qt::GestureFinished:   gs.assign("gesture finished"); break;
1173
            case Qt::GestureCanceled:   gs.assign("gesture canceled"); break;
1174
        }
1175
 
1176
        MSG_DEBUG("PinchGesture state " << gs << " detected");
1177
#endif
1178
        if (pg->state() == Qt::GestureFinished)
1179
        {
1180
            MSG_DEBUG("total scale: " << pg->totalScaleFactor() << ", scale: " << pg->scaleFactor() << ", last scale: " << pg->lastScaleFactor());
1181
 
1182
            if (pg->totalScaleFactor() > pg->scaleFactor())
1183
                settings();
1184
 
1185
            return true;
1186
        }
1187
    }
1188
    else if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
1189
    {
1190
        if (!gPageManager)
1191
            return false;
1192
 
1193
        QSwipeGesture *sw = static_cast<QSwipeGesture *>(swipe);
1194
        MSG_DEBUG("Swipe gesture detected.");
1195
 
1196
        if (sw->state() == Qt::GestureFinished)
1197
        {
1198
            if (sw->horizontalDirection() == QSwipeGesture::Left)
1199
                gPageManager->onSwipeEvent(TPageManager::SW_LEFT);
1200
            else if (sw->horizontalDirection() == QSwipeGesture::Right)
1201
                gPageManager->onSwipeEvent(TPageManager::SW_RIGHT);
1202
            else if (sw->verticalDirection() == QSwipeGesture::Up)
1203
                gPageManager->onSwipeEvent(TPageManager::SW_UP);
1204
            else if (sw->verticalDirection() == QSwipeGesture::Down)
1205
                gPageManager->onSwipeEvent(TPageManager::SW_DOWN);
1206
 
1207
            return true;
1208
        }
1209
    }
1210
 
1211
    return false;
1212
}
1213
 
1214
/**
1215
 * @brief MainWindow::mousePressEvent catches the event Qt::LeftButton.
1216
 *
1217
 * If the user presses the left mouse button somewhere in the main window, this
1218
 * method is triggered. It retrieves the position of the mouse pointer and
1219
 * sends it to the page manager TPageManager.
1220
 *
1221
 * @param event The event
1222
 */
1223
void MainWindow::mousePressEvent(QMouseEvent* event)
1224
{
1225
    DECL_TRACER("MainWindow::mousePressEvent(QMouseEvent* event)");
1226
 
1227
    if (!gPageManager)
1228
        return;
1229
 
1230
    if(event->button() == Qt::LeftButton)
1231
    {
1232
        int nx = 0, ny = 0;
1233
#ifdef Q_OS_IOS
1234
        if (mHaveNotchPortrait && gPageManager->getSettings()->isPortrait())
1235
        {
1236
            nx = mNotchPortrait.left();
1237
            ny = mNotchPortrait.top();
1238
        }
1239
        else if (mHaveNotchLandscape && gPageManager->getSettings()->isLandscape())
1240
        {
1241
            nx = mNotchLandscape.left();
1242
            ny = mNotchLandscape.top();
1243
        }
1244
        else
1245
        {
1246
            MSG_WARNING("Have no notch distances!");
1247
        }
1248
#endif
1249
        int x = static_cast<int>(event->position().x()) - nx;
1250
        int y = static_cast<int>(event->position().y()) - ny;
1251
        MSG_DEBUG("Mouse press coordinates: x: " << event->position().x() << ", y: " << event->position().y() << " [new x: " << x << ", y: " << y << " -- \"notch\" nx: " << nx << ", ny: " << ny << "]");
1252
 
1253
        mLastPressX = x;
1254
        mLastPressY = y;
1255
/*
1256
        QWidget *w = childAt(event->x(), event->y());
1257
 
1258
        if (w)
1259
        {
1260
            MSG_DEBUG("Object " << w->objectName().toStdString() << " is under mouse cursor.");
1261
            QObject *par = w->parent();
1262
 
1263
            if (par)
1264
            {
1265
                MSG_DEBUG("The parent is " << par->objectName().toStdString());
1266
 
1267
                if (par->objectName().startsWith("Item_"))
1268
                {
1269
                    QObject *ppar = par->parent();
1270
 
1271
                    if (ppar)
1272
                    {
1273
                        MSG_DEBUG("The pparent is " << ppar->objectName().toStdString());
1274
 
1275
                        if (ppar->objectName().startsWith("View_"))
1276
                        {
1277
                            QMouseEvent *mev = new QMouseEvent(event->type(), event->localPos(), event->globalPos(), event->button(), event->buttons(), event->modifiers());
1278
                            QApplication::postEvent(ppar, mev);
1279
                            return;
1280
                        }
1281
                    }
1282
                }
1283
            }
1284
        }
1285
*/
449 andreas 1286
        if (isScaled())
446 andreas 1287
        {
1288
            x = static_cast<int>(static_cast<double>(x) / mScaleFactor);
1289
            y = static_cast<int>(static_cast<double>(y) / mScaleFactor);
1290
        }
1291
 
1292
        gPageManager->mouseEvent(x, y, true);
1293
        mTouchStart = std::chrono::steady_clock::now();
1294
        mTouchX = mLastPressX;
1295
        mTouchY = mLastPressY;
1296
        event->accept();
1297
    }
1298
    else if (event->button() == Qt::MiddleButton)
1299
    {
1300
        event->accept();
1301
        settings();
1302
    }
1303
    else
1304
        QMainWindow::mousePressEvent(event);
1305
}
1306
 
1307
/**
1308
 * @brief MainWindow::mouseReleaseEvent catches the event Qt::LeftButton.
1309
 *
1310
 * If the user releases the left mouse button somewhere in the main window, this
1311
 * method is triggered. It retrieves the position of the mouse pointer and
1312
 * sends it to the page manager TPageManager.
1313
 *
1314
 * @param event The event
1315
 */
1316
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
1317
{
1318
    DECL_TRACER("MainWindow::mouseReleaseEvent(QMouseEvent* event)");
1319
 
1320
    if (!gPageManager)
1321
        return;
1322
 
1323
    if(event->button() == Qt::LeftButton)
1324
    {
1325
        int nx = 0, ny = 0;
1326
#ifdef Q_OS_IOS
1327
        if (mHaveNotchPortrait && gPageManager->getSettings()->isPortrait())
1328
        {
1329
            nx = mNotchPortrait.left();
1330
            ny = mNotchPortrait.top();
1331
        }
1332
        else if (mHaveNotchLandscape && gPageManager->getSettings()->isLandscape())
1333
        {
1334
            nx = mNotchLandscape.left();
1335
            ny = mNotchLandscape.top();
1336
        }
1337
#endif
1338
        int x = ((mLastPressX >= 0) ? mLastPressX : (static_cast<int>(event->position().x()) - nx));
1339
        int y = ((mLastPressY >= 0) ? mLastPressY : (static_cast<int>(event->position().y()) - ny));
1340
        MSG_DEBUG("Mouse press coordinates: x: " << event->position().x() << ", y: " << event->position().y());
1341
        mLastPressX = mLastPressY = -1;
1342
/*
1343
        QWidget *w = childAt(event->x(), event->y());
1344
 
1345
        if (w)
1346
        {
1347
            MSG_DEBUG("Object " << w->objectName().toStdString() << " is under mouse cursor.");
1348
            QObject *par = w->parent();
1349
 
1350
            if (par)
1351
            {
1352
                MSG_DEBUG("The parent is " << par->objectName().toStdString());
1353
 
1354
                if (par->objectName().startsWith("Item_"))
1355
                {
1356
                    QObject *ppar = par->parent();
1357
 
1358
                    if (ppar)
1359
                    {
1360
                        MSG_DEBUG("The pparent is " << ppar->objectName().toStdString());
1361
 
1362
                        if (ppar->objectName().startsWith("View_"))
1363
                        {
1364
                            QMouseEvent *mev = new QMouseEvent(event->type(), event->localPos(), event->globalPos(), event->button(), event->buttons(), event->modifiers());
1365
                            QApplication::postEvent(ppar, mev);
1366
                            return;
1367
                        }
1368
                    }
1369
                }
1370
            }
1371
        }
1372
*/
449 andreas 1373
        if (isScaled())
446 andreas 1374
        {
1375
            x = static_cast<int>(static_cast<double>(x) / mScaleFactor);
1376
            y = static_cast<int>(static_cast<double>(y) / mScaleFactor);
1377
        }
1378
 
1379
        gPageManager->mouseEvent(x, y, false);
1380
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
1381
        std::chrono::nanoseconds difftime = end - mTouchStart;
1382
        std::chrono::milliseconds msecs = std::chrono::duration_cast<std::chrono::milliseconds>(difftime);
1383
 
1384
        if (msecs.count() < 100)    // 1/10 of a second
1385
        {
1386
            MSG_DEBUG("Time was too short: " << msecs.count());
1387
            event->accept();
1388
            return;
1389
        }
1390
 
1391
        x = static_cast<int>(event->position().x());
1392
        y = static_cast<int>(event->position().y());
449 andreas 1393
        int width = scale(gPageManager->getSettings()->getWidth());
1394
        int height = scale(gPageManager->getSettings()->getHeight());
446 andreas 1395
        MSG_DEBUG("Coordinates: x1=" << mTouchX << ", y1=" << mTouchY << ", x2=" << x << ", y2=" << y << ", width=" << width << ", height=" << height);
1396
 
1397
        if (mTouchX < x && (x - mTouchX) > (width / 3))
1398
            gPageManager->onSwipeEvent(TPageManager::SW_RIGHT);
1399
        else if (x < mTouchX && (mTouchX - x) > (width / 3))
1400
            gPageManager->onSwipeEvent(TPageManager::SW_LEFT);
1401
        else if (mTouchY < y && (y - mTouchY) > (height / 3))
1402
            gPageManager->onSwipeEvent(TPageManager::SW_DOWN);
1403
        else if (y < mTouchY && (mTouchY - y) > (height / 3))
1404
            gPageManager->onSwipeEvent(TPageManager::SW_UP);
1405
 
1406
        event->accept();
1407
    }
1408
    else
1409
        QMainWindow::mouseReleaseEvent(event);
1410
}
1411
 
1412
void MainWindow::mouseMoveEvent(QMouseEvent* event)
1413
{
1414
    DECL_TRACER("MainWindow::mouseMoveEvent(QMouseEvent* event)");
1415
 
1416
    if (!gPageManager)
1417
        return;
1418
 
1419
    QWidget *w = childAt(event->position().x(), event->position().y());
1420
 
1421
    if (w)
1422
    {
1423
        MSG_DEBUG("Object " << w->objectName().toStdString() << " is under mouse cursor.");
1424
 
1425
        gPageManager->mouseMoveEvent(event->position().x(), event->position().y());
1426
        mLastPressX = event->position().x();
1427
        mLastPressY = event->position().y();
1428
    }
1429
/*        QObject *par = w->parent();
1430
 
1431
        if (par)
1432
        {
1433
            MSG_DEBUG("The parent is " << par->objectName().toStdString());
1434
 
1435
            if (par->objectName().startsWith("Item_"))
1436
            {
1437
                QObject *ppar = par->parent();
1438
 
1439
                if (ppar)
1440
                {
1441
                    MSG_DEBUG("The pparent is " << ppar->objectName().toStdString());
1442
 
1443
                    if (ppar->objectName().startsWith("View_"))
1444
                    {
1445
                        QMouseEvent *mev = new QMouseEvent(event->type(), event->localPos(), event->globalPos(), event->button(), event->buttons(), event->modifiers());
1446
                        QApplication::postEvent(ppar, mev);
1447
                        return;
1448
                    }
1449
                }
1450
            }
1451
        }
1452
    }
1453
*/
1454
}
1455
 
1456
void MainWindow::keyPressEvent(QKeyEvent *event)
1457
{
1458
    DECL_TRACER("MainWindow::keyPressEvent(QKeyEvent *event)");
1459
 
1460
    if (event && event->key() == Qt::Key_Back && !mToolbar)
1461
    {
1462
        QMessageBox msgBox(this);
1463
        msgBox.setText("Select what to do next:");
1464
        msgBox.addButton("Quit", QMessageBox::AcceptRole);
1465
        msgBox.addButton("Setup", QMessageBox::RejectRole);
1466
        msgBox.addButton("Cancel", QMessageBox::ResetRole);
1467
        int ret = msgBox.exec();
1468
 
1469
        if (ret == QMessageBox::Accepted)   // This is correct! QT seems to change here the buttons.
1470
        {
1471
            showSetup();
1472
            event->accept();
1473
            return;
1474
        }
1475
        else if (ret == QMessageBox::Rejected)  // This is correct! QT seems to change here the buttons.
1476
        {
1477
            event->accept();
1478
            close();
1479
        }
1480
    }
1481
    else
1482
        QMainWindow::keyPressEvent(event);
1483
}
1484
 
1485
void MainWindow::keyReleaseEvent(QKeyEvent *event)
1486
{
1487
    DECL_TRACER("MainWindow::keyReleaseEvent(QKeyEvent *event)");
1488
 
1489
    QMainWindow::keyReleaseEvent(event);
1490
}
1491
 
1492
/**
1493
 * @brief MainWindow::onScreenOrientationChanged sets invers or normal orientation.
1494
 * This method sets according to the actual set orientation the invers
1495
 * orientations or switches back to normal orientation.
1496
 * For example: When the orientation is set to portrait and the device is turned
1497
 * to top down, then the orientation is set to invers portrait.
1498
 * @param ori   The detected orientation
1499
 */
1500
void MainWindow::onScreenOrientationChanged(Qt::ScreenOrientation ori)
1501
{
1502
    DECL_TRACER("MainWindow::onScreenOrientationChanged(int ori)");
1503
#if defined(QT_DEBUG) && (defined(Q_OS_IOS) || defined(Q_OS_ANDROID))
1504
    MSG_DEBUG("Orientation changed to " << orientationToString(ori) << " (mOrientation: " << orientationToString(mOrientation) << ")");
1505
#endif
1506
    if (!gPageManager)
1507
        return;
1508
 
1509
    if (gPageManager->getSettings()->isPortrait())
1510
    {
1511
#ifdef Q_OS_IOS
1512
        if (!mHaveNotchPortrait)
1513
            setNotch();
1514
#endif
1515
        if (ori == Qt::PortraitOrientation || ori == Qt::InvertedPortraitOrientation)
1516
        {
1517
#ifdef Q_OS_IOS
1518
            if (mSensor)
1519
                mSensor->setCurrentOrientation(ori);
1520
#endif
1521
            if (mOrientation == ori)
1522
                return;
1523
 
1524
            mOrientation = ori;
1525
        }
1526
        else if (mOrientation != Qt::PortraitOrientation && mOrientation != Qt::InvertedPortraitOrientation)
1527
            mOrientation = Qt::PortraitOrientation;
1528
    }
1529
    else
1530
    {
1531
#ifdef Q_OS_IOS
1532
        if (!mHaveNotchLandscape)
1533
            setNotch();
1534
#endif
1535
        if (ori == Qt::LandscapeOrientation || ori == Qt::InvertedLandscapeOrientation)
1536
        {
1537
#ifdef Q_OS_IOS
1538
            if (mSensor)
1539
                mSensor->setCurrentOrientation(ori);
1540
#endif
1541
            if (mOrientation == ori)
1542
                return;
1543
 
1544
            mOrientation = ori;
1545
        }
1546
        else if (mOrientation != Qt::LandscapeOrientation && mOrientation != Qt::InvertedLandscapeOrientation)
1547
            mOrientation = Qt::LandscapeOrientation;
1548
    }
1549
 
1550
    J_ORIENTATION jori = O_UNDEFINED;
1551
 
1552
    switch(mOrientation)
1553
    {
1554
        case Qt::LandscapeOrientation:          jori = O_LANDSCAPE; break;
1555
        case Qt::InvertedLandscapeOrientation:  jori = O_REVERSE_LANDSCAPE; break;
1556
        case Qt::PortraitOrientation:           jori = O_PORTRAIT; break;
1557
        case Qt::InvertedPortraitOrientation:   jori = O_REVERSE_PORTRAIT; break;
1558
        default:
1559
            return;
1560
    }
1561
 
1562
    _setOrientation(jori);
1563
#ifdef Q_OS_IOS
1564
    setNotch();
1565
#endif
1566
}
1567
#ifdef Q_OS_IOS
1568
/**
1569
 * @brief MainWindow::onPositionUpdated
1570
 * This method is a callback function for the Qt framework. It is called
1571
 * whenever the geo position changes. The position information is never really
1572
 * used and is implemented only to keep the application on IOS running in the
1573
 * background.
1574
 *
1575
 * @param update    A structure containing the geo position information.
1576
 */
1577
 
1578
void MainWindow::onPositionUpdated(const QGeoPositionInfo &update)
1579
{
1580
    DECL_TRACER("MainWindow::onPositionUpdated(const QGeoPositionInfo &update)");
1581
 
1582
    QGeoCoordinate coord = update.coordinate();
1583
    MSG_DEBUG("Geo location: " << coord.toString().toStdString());
1584
}
1585
 
1586
void MainWindow::onErrorOccurred(QGeoPositionInfoSource::Error positioningError)
1587
{
1588
    DECL_TRACER("MainWindow::onErrorOccurred(QGeoPositionInfoSource::Error positioningError)");
1589
 
1590
    switch(positioningError)
1591
    {
1592
        case QGeoPositionInfoSource::AccessError:
1593
            MSG_ERROR("The connection setup to the remote positioning backend failed because the application lacked the required privileges.");
1594
            mGeoHavePermission = false;
1595
        break;
1596
 
1597
        case QGeoPositionInfoSource::ClosedError:   MSG_ERROR("The remote positioning backend closed the connection, which happens for example in case the user is switching location services to off. As soon as the location service is re-enabled regular updates will resume."); break;
1598
        case QGeoPositionInfoSource::UnknownSourceError: MSG_ERROR("An unidentified error occurred."); break;
1599
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1600
        case QGeoPositionInfoSource::UpdateTimeoutError: MSG_ERROR("Current position could not be retrieved within the specified timeout."); break;
1601
#endif
1602
        default:
1603
            return;
1604
    }
1605
}
1606
#endif  // Q_OS_IOS
1607
/**
1608
 * @brief Displays or hides a phone dialog window.
1609
 * This method creates and displays a phone dialog window containing everything
1610
 * a simple phone needs. Depending on the parameter \p state the dialog is
1611
 * created or an existing dialog is closed.
1612
 * @param state     If TRUE the dialog is created or if it is not visible
1613
 * brought to front and is made visible.
1614
 * If this is FALSE an existing dialog window is destroid and disappears. If
1615
 * the window didn't exist nothing happens.
1616
 */
1617
void MainWindow::showPhoneDialog(bool state)
1618
{
1619
    DECL_TRACER("MainWindow::showPhoneDialog(bool state)");
1620
 
1621
    if (mPhoneDialog)
1622
    {
1623
        if (!state)
1624
        {
1625
            mPhoneDialog->close();
1626
            delete mPhoneDialog;
1627
            mPhoneDialog = nullptr;
1628
            return;
1629
        }
1630
 
1631
        if (!mPhoneDialog->isVisible())
1632
            mPhoneDialog->setVisible(true);
1633
 
1634
        return;
1635
    }
1636
 
1637
    if (!state)
1638
        return;
1639
 
1640
    mPhoneDialog = new TQtPhone(this);
1641
#if defined(Q_OS_ANDROID)
1642
    // On mobile devices we set the scale factor always because otherwise the
1643
    // dialog will be unusable.
1644
    mPhoneDialog->setScaleFactor(gScale);
1645
    mPhoneDialog->doResize();
1646
#endif
1647
    mPhoneDialog->open();
1648
}
1649
 
1650
/**
1651
 * Displays a phone number (can also be an URL) on a label in the phone dialog
1652
 * window.
1653
 * @param number    The string contains the phone number to display.
1654
 */
1655
void MainWindow::setPhoneNumber(const std::string& number)
1656
{
1657
    DECL_TRACER("MainWindow::setPhoneNumber(const std::string& number)");
1658
 
1659
    if (!mPhoneDialog)
1660
        return;
1661
 
1662
    mPhoneDialog->setPhoneNumber(number);
1663
}
1664
 
1665
/**
1666
 * Displays a message in the status line on the bottom of the phone dialog
1667
 * window.
1668
 * @param msg   The string conaining a message.
1669
 */
1670
void MainWindow::setPhoneStatus(const std::string& msg)
1671
{
1672
    DECL_TRACER("MainWindow::setPhoneStatus(const std::string& msg)");
1673
 
1674
    if (!mPhoneDialog)
1675
        return;
1676
 
1677
    mPhoneDialog->setPhoneStatus(msg);
1678
}
1679
 
1680
void MainWindow::setPhoneState(int state, int id)
1681
{
1682
    DECL_TRACER("MainWindow::setPhoneState(int state)");
1683
 
1684
    if (mPhoneDialog)
1685
        mPhoneDialog->setPhoneState(state, id);
1686
}
1687
 
1688
/**
1689
 * @brief MainWindow::createActions creates the toolbar on the right side.
1690
 */
1691
void MainWindow::createActions()
1692
{
1693
    DECL_TRACER("MainWindow::createActions()");
1694
 
1695
    // If the toolbar should not be visible at all we return here immediately.
1696
    if (TConfig::getToolbarSuppress())
1697
        return;
1698
 
1699
    // Add a mToolbar (on the right side)
1700
    mToolbar = new QToolBar(this);
1701
    QPalette palette(mToolbar->palette());
1702
    palette.setColor(QPalette::Window, QColorConstants::Svg::honeydew);
1703
    mToolbar->setPalette(palette);
1704
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
1705
    mToolbar->setAllowedAreas(Qt::RightToolBarArea);
1706
    mToolbar->setFloatable(false);
1707
    mToolbar->setMovable(false);
1708
#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1709
    if (isScaled())
1710
    {
1711
        int width = (int)((double)gPageManager->getSettings()->getWidth() * gScale);
1712
        int tbWidth = (int)(48.0 * gScale);
1713
        int icWidth = (int)(40.0 * gScale);
1714
 
1715
        if ((gFullWidth - width) < tbWidth && !TConfig::getToolbarForce())
1716
        {
1717
            delete mToolbar;
1718
            mToolbar = nullptr;
1719
            return;
1720
        }
1721
 
1722
        MSG_DEBUG("Icon size: " << icWidth << "x" << icWidth << ", Toolbar width: " << tbWidth);
1723
        QSize iSize(icWidth, icWidth);
1724
        mToolbar->setIconSize(iSize);
1725
    }
1726
#endif  // QT_VERSION
1727
#if (defined(Q_OS_ANDROID) || defined(Q_OS_IOS)) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1728
    if (isScaled())
1729
    {
1730
        int panwidth = (int)((double)gPageManager->getSettings()->getWidth() * gScale);
1731
        int toolwidth = mToolbar->width();
1732
 
1733
        if ((gFullWidth - panwidth) < toolwidth && !TConfig::getToolbarForce())
1734
        {
1735
            delete mToolbar;
1736
            mToolbar = nullptr;
1737
            return;
1738
        }
1739
    }
1740
#endif
1741
#else
1742
    mToolbar->setFloatable(true);
1743
    mToolbar->setMovable(true);
1744
    mToolbar->setAllowedAreas(Qt::RightToolBarArea | Qt::BottomToolBarArea);
1745
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
1746
    QAction *arrowUpAct = new QAction(QIcon(":/images/arrow_up.png"), tr("Up"), this);
1747
    connect(arrowUpAct, &QAction::triggered, this, &MainWindow::arrowUp);
1748
    mToolbar->addAction(arrowUpAct);
1749
 
1750
    QAction *arrowLeftAct = new QAction(QIcon(":/images/arrow_left.png"), tr("Left"), this);
1751
    connect(arrowLeftAct, &QAction::triggered, this, &MainWindow::arrowLeft);
1752
    mToolbar->addAction(arrowLeftAct);
1753
 
1754
    QAction *arrowRightAct = new QAction(QIcon(":/images/arrow_right.png"), tr("Right"), this);
1755
    connect(arrowRightAct, &QAction::triggered, this, &MainWindow::arrowRight);
1756
    mToolbar->addAction(arrowRightAct);
1757
 
1758
    QAction *arrowDownAct = new QAction(QIcon(":/images/arrow_down.png"), tr("Down"), this);
1759
    connect(arrowDownAct, &QAction::triggered, this, &MainWindow::arrowDown);
1760
    mToolbar->addAction(arrowDownAct);
1761
 
1762
    QAction *selectOkAct = new QAction(QIcon(":/images/ok.png"), tr("Ok"), this);
1763
    connect(selectOkAct, &QAction::triggered, this, &MainWindow::selectOk);
1764
    mToolbar->addAction(selectOkAct);
1765
 
1766
    mToolbar->addSeparator();
1767
 
1768
    QToolButton *btVolUp = new QToolButton(this);
1769
    btVolUp->setIcon(QIcon(":/images/vol_up.png"));
1770
    connect(btVolUp, &QToolButton::pressed, this, &MainWindow::volumeUpPressed);
1771
    connect(btVolUp, &QToolButton::released, this, &MainWindow::volumeUpReleased);
1772
    mToolbar->addWidget(btVolUp);
1773
 
1774
    QToolButton *btVolDown = new QToolButton(this);
1775
    btVolDown->setIcon(QIcon(":/images/vol_down.png"));
1776
    connect(btVolDown, &QToolButton::pressed, this, &MainWindow::volumeDownPressed);
1777
    connect(btVolDown, &QToolButton::released, this, &MainWindow::volumeDownReleased);
1778
    mToolbar->addWidget(btVolDown);
1779
/*
1780
    QAction *volMute = new QAction(QIcon(":/images/vol_mute.png"), tr("X"), this);
1781
    connect(volMute, &QAction::triggered, this, &MainWindow::volumeMute);
1782
    mToolbar->addAction(volMute);
1783
*/
1784
    mToolbar->addSeparator();
1785
    const QIcon settingsIcon = QIcon::fromTheme("settings-configure", QIcon(":/images/settings.png"));
1786
    QAction *settingsAct = new QAction(settingsIcon, tr("&Settings..."), this);
1787
    settingsAct->setStatusTip(tr("Change the settings"));
1788
    connect(settingsAct, &QAction::triggered, this, &MainWindow::settings);
1789
    mToolbar->addAction(settingsAct);
1790
 
1791
    const QIcon aboutIcon = QIcon::fromTheme("help-about", QIcon(":/images/info.png"));
1792
    QAction *aboutAct = new QAction(aboutIcon, tr("&About..."), this);
1793
    aboutAct->setShortcuts(QKeySequence::Open);
1794
    aboutAct->setStatusTip(tr("About this program"));
1795
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
1796
    mToolbar->addAction(aboutAct);
1797
 
1798
    const QIcon exitIcon = QIcon::fromTheme("application-exit", QIcon(":/images/off.png"));
1799
    QAction *exitAct = mToolbar->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
1800
    exitAct->setShortcuts(QKeySequence::Quit);
1801
    exitAct->setStatusTip(tr("Exit the application"));
1802
 
1803
    addToolBar(Qt::RightToolBarArea, mToolbar);
1804
}
1805
 
1806
/**
1807
 * @brief MainWindow::settings initiates the configuration dialog.
1808
 */
1809
void MainWindow::settings()
1810
{
1811
    DECL_TRACER("MainWindow::settings()");
1812
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
1813
#ifdef Q_OS_ANDROID
1814
    if (gPageManager)
1815
    {
1816
        gPageManager->showSetup();
1817
        return;
1818
    }
1819
    else    // This "else" should never be executed!
1820
        displayMessage("<b>Fatal error</b>: An internal mandatory class was not initialized!<br>Unable to show setup dialog!", "Fatal error");
1821
#else
1822
    mIOSSettingsActive = true;
1823
    QASettings::openSettings();
1824
#endif
1825
#else
1826
    // Save some old values to decide whether to start over or not.
1827
    string oldHost = TConfig::getController();
1828
    int oldPort = TConfig::getPort();
1829
    int oldChannelID = TConfig::getChannel();
1830
    string oldSurface = TConfig::getFtpSurface();
1831
    bool oldToolbar = TConfig::getToolbarForce();
1832
    bool oldToolbarSuppress = TConfig::getToolbarSuppress();
1833
    // Initialize and open the settings dialog.
1834
    TQtSettings *dlg_settings = new TQtSettings(this);
1835
    int ret = dlg_settings->exec();
1836
    bool rebootAnyway = false;
1837
 
1838
    if ((ret && dlg_settings->hasChanged()) || (ret && dlg_settings->downloadForce()))
1839
    {
1840
        writeSettings();
1841
 
1842
        if (!TConfig::getToolbarSuppress() && oldToolbar != TConfig::getToolbarForce())
1843
        {
1844
            QMessageBox msgBox(this);
1845
            msgBox.setText("The change for the visibility of the toolbar will be active on the next start of TPanel!");
1846
            msgBox.exec();
1847
        }
1848
        else if (oldToolbarSuppress != TConfig::getToolbarSuppress() && TConfig::getToolbarSuppress())
1849
        {
1850
            if (mToolbar)
1851
            {
1852
                mToolbar->close();
1853
                delete mToolbar;
1854
                mToolbar = nullptr;
1855
            }
1856
        }
1857
 
1858
        if (TConfig::getFtpSurface() != oldSurface || dlg_settings->downloadForce())
1859
        {
1860
            bool dlYes = true;
1861
 
1862
            MSG_DEBUG("Surface should be downloaded (Old: " << oldSurface << ", New: " << TConfig::getFtpSurface() << ")");
1863
 
1864
            if (!dlg_settings->downloadForce())
1865
            {
1866
                QMessageBox msgBox(this);
1867
                msgBox.setText(QString("Should the surface <b>") + TConfig::getFtpSurface().c_str() + "</b> be installed?");
1868
                msgBox.addButton(QMessageBox::Yes);
1869
                msgBox.addButton(QMessageBox::No);
1870
                int ret = msgBox.exec();
1871
 
1872
                if (ret == QMessageBox::No)
1873
                    dlYes = false;
1874
            }
1875
 
1876
            if (dlYes)
1877
            {
1878
                TTPInit tpinit;
1879
                tpinit.regCallbackProcessEvents(bind(&MainWindow::runEvents, this));
1880
                tpinit.regCallbackProgressBar(bind(&MainWindow::_onProgressChanged, this, std::placeholders::_1));
1881
                tpinit.setPath(TConfig::getProjectPath());
1882
                tpinit.setFileSize(static_cast<off64_t>(dlg_settings->getSelectedFtpFileSize()));
1883
                string msg = "Loading file <b>" + TConfig::getFtpSurface() + "</b>.";
1884
                MSG_DEBUG("Download of surface " << TConfig::getFtpSurface() << " was forced!");
1885
 
1886
                downloadBar(msg, this);
1887
 
1888
                if (tpinit.loadSurfaceFromController(true))
1889
                    rebootAnyway = true;
1890
 
1891
                mDownloadBar->close();
1892
                mBusy = false;
1893
            }
1894
            else
1895
            {
1896
                MSG_DEBUG("No change of surface. Old surface " << oldSurface << " was saved again.");
1897
                TConfig::saveFtpSurface(oldSurface);
1898
                writeSettings();
1899
            }
1900
        }
1901
 
1902
        if (TConfig::getController() != oldHost ||
1903
            TConfig::getChannel() != oldChannelID ||
1904
            TConfig::getPort() != oldPort || rebootAnyway)
1905
        {
1906
            // Start over by exiting this class
1907
            MSG_INFO("Program will start over!");
1908
            _restart_ = true;
1909
            prg_stopped = true;
1910
            killed = true;
1911
 
1912
            if (gAmxNet)
1913
                gAmxNet->stop();
1914
 
1915
            close();
1916
        }
1917
    }
1918
    else if (!ret && dlg_settings->hasChanged())
1919
    {
1920
        TConfig cf(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
1921
    }
1922
 
1923
    delete dlg_settings;
1924
#endif  // defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
1925
}
1926
 
1927
/**
1928
 * @brief MainWindow::writeSettings Writes the settings into the configuration file.
1929
 */
1930
void MainWindow::writeSettings()
1931
{
1932
    DECL_TRACER("MainWindow::writeSettings()");
1933
 
1934
    TConfig::saveSettings();
1935
    MSG_INFO("Wrote settings.");
1936
}
1937
 
1938
/**
1939
 * @brief MainWindow::about displays the _about_ dialog.
1940
 */
1941
void MainWindow::about()
1942
{
1943
    DECL_TRACER("MainWindow::about()");
1944
 
1945
#ifdef Q_OS_IOS
1946
    // On IOS the explicit about dialog is shown over the whole screen with
1947
    // the text in a small stripe on the left. This looks ugly and therefor
1948
    // we construct our own about dialog.
1949
    std::string msg = "About TPanel\n\n";
486 andreas 1950
    msg.append("Implementation of an AMX G4/G5 panel\n");
446 andreas 1951
    msg.append("Version v").append(VERSION_STRING()).append("\n");
486 andreas 1952
    msg.append("(C) Copyright 2020 to 2025 by Andreas Theofilu (andreas@theosys.at)\n");
446 andreas 1953
 
1954
    QMessageBox about(this);
1955
    about.addButton(QMessageBox::Ok);
1956
    about.setWindowTitle(tr("About TPanel"));
1957
    about.setIconPixmap(QPixmap(":images/icon.png"));
1958
    about.setTextFormat(Qt::PlainText);
1959
    about.setText(tr(msg.c_str()));
1960
    about.setInformativeText(tr("This program is under the terms of GPL version 3!"));
1961
    about.exec();
1962
#else
486 andreas 1963
    std::string msg = "Implementation of an AMX G4/G5 panel\n";
446 andreas 1964
    msg.append("Version v").append(VERSION_STRING()).append("\n");
486 andreas 1965
    msg.append("(C) Copyright 2020 to 2025 by Andreas Theofilu <andreas@theosys.at>\n");
446 andreas 1966
    msg.append("This program is under the terms of GPL version 3!");
1967
    QMessageBox::about(this, tr("About TPanel"), tr(msg.c_str()));
1968
#endif
1969
}
1970
 
1971
void MainWindow::arrowUp()
1972
{
1973
    DECL_TRACER("MainWindow::arrowUp()");
1974
 
1975
    extButtons_t btType = EXT_CURSOR_UP;
1976
 
1977
    if (TConfig::getPanelType().find("Android") != string::npos)
1978
        btType = EXT_GESTURE_UP;
1979
 
1980
    gPageManager->externalButton(btType, true);
1981
    gPageManager->externalButton(btType, false);
1982
}
1983
 
1984
void MainWindow::arrowLeft()
1985
{
1986
    DECL_TRACER("MainWindow::arrowLeft()");
1987
    extButtons_t btType = EXT_CURSOR_LEFT;
1988
 
1989
    if (TConfig::getPanelType().find("Android") != string::npos)
1990
        btType = EXT_GESTURE_LEFT;
1991
 
1992
    gPageManager->externalButton(btType, true);
1993
    gPageManager->externalButton(btType, false);
1994
}
1995
 
1996
void MainWindow::arrowRight()
1997
{
1998
    DECL_TRACER("MainWindow::arrowRight()");
1999
    extButtons_t btType = EXT_CURSOR_RIGHT;
2000
 
2001
    if (TConfig::getPanelType().find("Android") != string::npos)
2002
        btType = EXT_GESTURE_RIGHT;
2003
 
2004
    gPageManager->externalButton(btType, true);
2005
    gPageManager->externalButton(btType, false);
2006
}
2007
 
2008
void MainWindow::arrowDown()
2009
{
2010
    DECL_TRACER("MainWindow::arrowDown()");
2011
    extButtons_t btType = EXT_CURSOR_DOWN;
2012
 
2013
    if (TConfig::getPanelType().find("Android") != string::npos)
2014
        btType = EXT_GESTURE_DOWN;
2015
 
2016
    gPageManager->externalButton(btType, true);
2017
    gPageManager->externalButton(btType, false);
2018
}
2019
 
2020
void MainWindow::selectOk()
2021
{
2022
    DECL_TRACER("MainWindow::selectOk()");
2023
    extButtons_t btType = EXT_CURSOR_SELECT;
2024
 
2025
    if (TConfig::getPanelType().find("Android") != string::npos)
2026
        btType = EXT_GESTURE_DOUBLE_PRESS;
2027
 
2028
    gPageManager->externalButton(btType, true);
2029
    gPageManager->externalButton(btType, false);
2030
}
2031
 
2032
void MainWindow::volumeUpPressed()
2033
{
2034
    DECL_TRACER("MainWindow::volumeUpPressed()");
2035
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
2036
 
2037
    if (TConfig::getPanelType().find("Android") != string::npos)
2038
        btType = EXT_GESTURE_ROTATE_RIGHT;
2039
 
2040
    gPageManager->externalButton(btType, true);
2041
}
2042
 
2043
void MainWindow::volumeUpReleased()
2044
{
2045
    DECL_TRACER("MainWindow::volumeUpReleased()");
2046
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
2047
 
2048
    if (TConfig::getPanelType().find("Android") != string::npos)
2049
        btType = EXT_GESTURE_ROTATE_RIGHT;
2050
 
2051
    gPageManager->externalButton(btType, false);
2052
}
2053
 
2054
void MainWindow::volumeDownPressed()
2055
{
2056
    DECL_TRACER("MainWindow::volumeDownPressed()");
2057
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
2058
 
2059
    if (TConfig::getPanelType().find("Android") != string::npos)
2060
        btType = EXT_GESTURE_ROTATE_LEFT;
2061
 
2062
    gPageManager->externalButton(btType, true);
2063
}
2064
 
2065
void MainWindow::volumeDownReleased()
2066
{
2067
    DECL_TRACER("MainWindow::volumeDownReleased()");
2068
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
2069
 
2070
    if (TConfig::getPanelType().find("Android") != string::npos)
2071
        btType = EXT_GESTURE_ROTATE_LEFT;
2072
 
2073
    gPageManager->externalButton(btType, false);
2074
}
2075
/*
2076
void MainWindow::volumeMute()
2077
{
2078
    DECL_TRACER("MainWindow::volumeMute()");
2079
    gPageManager->externalButton(EXT_GENERAL, true);
2080
    gPageManager->externalButton(EXT_GENERAL, false);
2081
}
2082
*/
2083
 
2084
void MainWindow::animationInFinished()
2085
{
2086
    DECL_TRACER("MainWindow::animationInFinished()");
2087
 
2088
    if (mAnimObjects.empty())
2089
    {
2090
#if TESTMODE == 1
2091
        setScreenDone();
2092
#endif
2093
        return;
2094
    }
2095
 
2096
//    TLOCKER(anim_mutex);
2097
    map<ulong, OBJECT_t *>::iterator iter;
2098
 
2099
    for (iter = mAnimObjects.begin(); iter != mAnimObjects.end(); ++iter)
2100
    {
2101
        if (!iter->second->animation)
2102
            continue;
2103
 
2104
        if (!iter->second->invalid && iter->second->type == OBJ_SUBPAGE && iter->second->animation->state() == QAbstractAnimation::Stopped)
2105
        {
2106
            if (iter->second->object.widget)
2107
            {
2108
                iter->second->object.widget->lower();
2109
                iter->second->object.widget->show();
2110
                iter->second->object.widget->raise();
2111
            }
2112
 
2113
            disconnect(iter->second->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
2114
            delete iter->second->animation;
2115
            iter->second->animation = nullptr;
2116
        }
2117
    }
2118
 
2119
    // Delete all empty/finished animations
2120
    bool repeat = false;
2121
 
2122
    do
2123
    {
2124
        repeat = false;
2125
 
2126
        for (iter = mAnimObjects.begin(); iter != mAnimObjects.end(); ++iter)
2127
        {
2128
            if (!iter->second->remove && !iter->second->animation)
2129
            {
2130
                mAnimObjects.erase(iter);
2131
                repeat = true;
2132
                break;
2133
            }
2134
        }
2135
    }
2136
    while (repeat);
2137
#if TESTMODE == 1
2138
    __success = true;
2139
    setScreenDone();
2140
#endif
2141
}
2142
 
2143
void MainWindow::animationFinished()
2144
{
2145
    DECL_TRACER("MainWindow::animationFinished()");
2146
 
2147
    if (mAnimObjects.empty())
2148
    {
2149
#if TESTMODE == 1
2150
        setScreenDone();
2151
#endif
2152
        return;
2153
    }
2154
 
2155
//    TLOCKER(anim_mutex);
2156
    map<ulong, OBJECT_t *>::iterator iter;
2157
 
2158
    for (iter = mAnimObjects.begin(); iter != mAnimObjects.end(); ++iter)
2159
    {
2160
        OBJECT_t *obj = findObject(iter->first);
2161
 
2162
        if (obj && obj->remove && obj->animation && obj->animation->state() == QAbstractAnimation::Stopped)
2163
        {
2164
            MSG_DEBUG("Invalidating object " << handleToString(iter->first));
2165
            disconnect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
2166
            delete obj->animation;
2167
            obj->animation = nullptr;
2168
            invalidateAllSubObjects(iter->first);
2169
            invalidateObject(iter->first);
2170
 
2171
            if (obj->type == OBJ_SUBPAGE && obj->object.widget)
2172
                obj->object.widget->hide();
2173
        }
2174
    }
2175
    // Delete all empty/finished animations
2176
    bool repeat = false;
2177
 
2178
    do
2179
    {
2180
        repeat = false;
2181
 
2182
        for (iter = mAnimObjects.begin(); iter != mAnimObjects.end(); ++iter)
2183
        {
2184
            if (iter->second->remove && !iter->second->animation)
2185
            {
2186
                mAnimObjects.erase(iter);
2187
                repeat = true;
2188
                break;
2189
            }
2190
        }
2191
    }
2192
    while (repeat);
2193
#if TESTMODE == 1
2194
    __success = true;
2195
    setScreenDone();
2196
#endif
2197
}
2198
 
2199
void MainWindow::repaintWindows()
2200
{
2201
    DECL_TRACER("MainWindow::repaintWindows()");
2202
 
2203
    if (mWasInactive)
2204
    {
2205
        MSG_INFO("Refreshing of visible popups will be requested.");
2206
        mDoRepaint = true;
2207
    }
2208
}
2209
 
2210
void MainWindow::toFront(ulong handle)
2211
{
2212
    DECL_TRACER("MainWindow::toFront(ulong handle)");
2213
 
2214
    TObject::OBJECT_t *obj = findObject(handle);
2215
 
2216
    if (!obj)
2217
    {
2218
        MSG_WARNING("Object with " << handleToString(handle) << " not found!");
2219
#if TESTMODE == 1
2220
        setScreenDone();
2221
#endif
2222
        return;
2223
    }
2224
 
2225
    if (obj->type == TObject::OBJ_SUBPAGE && obj->object.widget)
2226
        obj->object.widget->raise();
2227
#if TESTMODE == 1
2228
    __success = true;
2229
    setScreenDone();
2230
#endif
2231
}
2232
 
2233
void MainWindow::downloadSurface(const string &file, size_t size)
2234
{
2235
    DECL_TRACER("MainWindow::downloadSurface(const string &file, size_t size)");
2236
 
2237
    if (mBusy)
2238
        return;
2239
 
2240
    QMessageBox msgBox(this);
2241
    msgBox.setText(QString("Should the surface <b>") + file.c_str() + "</b> be installed?<br><i><u>Hint</u>: This will also save all current made settings.</i>");
2242
    msgBox.addButton(QMessageBox::Yes);
2243
    msgBox.addButton(QMessageBox::No);
2244
    int ret = msgBox.exec();
2245
 
2246
    if (ret == QMessageBox::Yes)
2247
    {
2248
        TTPInit tpinit;
2249
        tpinit.regCallbackProcessEvents(bind(&MainWindow::runEvents, this));
2250
        tpinit.regCallbackProgressBar(bind(&MainWindow::_onProgressChanged, this, std::placeholders::_1));
2251
        tpinit.setPath(TConfig::getProjectPath());
2252
 
2253
        if (size)
2254
            tpinit.setFileSize(static_cast<off64_t>(size));
2255
        else
2256
        {
2257
            size = static_cast<size_t>(tpinit.getFileSize(file));
2258
 
2259
            if (!size)
2260
            {
2261
                displayMessage("File <b>" + file + "</b> either doesn't exist on " + TConfig::getController() + " or the NetLinx is not reachable!", "Error");
2262
                return;
2263
            }
2264
 
2265
            tpinit.setFileSize(static_cast<off64_t>(size));
2266
        }
2267
 
2268
        string msg = "Loading file <b>" + file + "</b>.";
2269
 
2270
        downloadBar(msg, this);
2271
        bool reboot = false;
2272
 
2273
        if (tpinit.loadSurfaceFromController(true))
2274
            reboot = true;
2275
        else
2276
            displayMessage("Error downloading file <b>" + file + "</b>!", "Error");
2277
 
2278
        mDownloadBar->close();
2279
        TConfig::setTemporary(true);
2280
        TConfig::saveSettings();
2281
 
2282
        if (reboot)
2283
        {
2284
            // Start over by exiting this class
2285
            MSG_INFO("Program will start over!");
2286
            _restart_ = true;
2287
            prg_stopped = true;
2288
            killed = true;
2289
 
2290
            if (gAmxNet)
2291
                gAmxNet->stop();
2292
 
2293
            close();
2294
        }
2295
    }
2296
 
2297
    mBusy = false;
2298
}
2299
 
2300
void MainWindow::displayMessage(const string &msg, const string &title)
2301
{
2302
    DECL_TRACER("MainWindow::displayMessage(const string &msg, const string &title)");
2303
 
2304
    QMessageBox msgBox(this);
2305
    msgBox.setText(msg.c_str());
2306
 
2307
    if (!title.empty())
2308
        msgBox.setWindowTitle(title.c_str());
2309
 
2310
    msgBox.setWindowModality(Qt::WindowModality::ApplicationModal);
2311
    msgBox.addButton(QMessageBox::Ok);
2312
    msgBox.exec();
2313
}
2314
 
2315
void MainWindow::askPassword(ulong handle, const string msg, const string& title, int x, int y)
2316
{
2317
    DECL_TRACER("MainWindow::askPassword(const string msg, const string& title, int x, int y)");
2318
 
2319
    TQtInputLine *inputLine = new TQtInputLine(this);
2320
    inputLine->setMessage(msg);
2321
    inputLine->setWindowTitle(title.c_str());
2322
    inputLine->setWindowModality(Qt::WindowModality::ApplicationModal);
2323
    inputLine->setPassword(true);
2324
    int bt = inputLine->exec();
2325
 
2326
    if (bt == QDialog::Rejected)
2327
    {
2328
        if (gPageManager)
2329
            gPageManager->callSetPassword(handle, "", x, y);
2330
 
2331
        delete inputLine;
2332
        return;
2333
    }
2334
 
2335
    if (gPageManager)
2336
        gPageManager->callSetPassword(handle, inputLine->getText(), x, y);
2337
 
2338
    delete inputLine;
2339
}
2340
 
2341
void MainWindow::fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)
2342
{
2343
    DECL_TRACER("MainWindow::fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)");
2344
 
2345
    std::string pt = path;
2346
 
2347
    if (fs::exists(path) && fs::is_regular_file(path))
2348
    {
2349
        size_t pos = pt.find_last_of("/");
2350
 
2351
        if (pos != std::string::npos)
2352
            pt = pt.substr(0, pos);
2353
        else
2354
        {
2355
            char hv0[4096];
2356
            getcwd(hv0, sizeof(hv0));
2357
            pt = hv0;
2358
        }
2359
    }
2360
 
2361
    QString actPath(pt.c_str());
2362
    QFileDialog fdialog(this, tr("File"), actPath, tr(extension.c_str()));
2363
    fdialog.setAcceptMode(QFileDialog::AcceptSave);
2364
 
2365
    if (!suffix.empty())
2366
        fdialog.setDefaultSuffix(suffix.c_str());
2367
 
2368
    fdialog.setOption(QFileDialog::DontConfirmOverwrite);
2369
    QString fname;
2370
 
2371
    if (fdialog.exec())
2372
    {
2373
        QDir dir = fdialog.directory();
2374
        QStringList list = fdialog.selectedFiles();
2375
 
2376
        if (list.size() > 0)
2377
            fname = dir.absoluteFilePath(list.at(0));
2378
        else
2379
            return;
2380
    }
2381
    else
2382
        return;
2383
 
2384
#ifdef Q_OS_ANDROID
2385
    // In case of Android we get some kind of URL instead of a clear
2386
    // path. Because of this we must call some Java API functions to find the
2387
    // real path.
2388
    QString fileName = fname;
2389
 
2390
    if (fileName.contains("content://"))
2391
    {
2392
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2393
        QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod(
2394
            "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;",
2395
            QAndroidJniObject::fromString(fileName).object<jstring>());
2396
 
2397
        fileName = QAndroidJniObject::callStaticObjectMethod(
2398
            "org/qtproject/theosys/UriToPath", "getFileName",
2399
            "(Landroid/net/Uri;Landroid/content/Context;)Ljava/lang/String;",
2400
            uri.object(), QtAndroid::androidContext().object()).toString();
2401
#else   // QT5_LINUX
2402
        // For QT6 the API has slightly changed. Especialy the class name to
2403
        // call Java objects.
2404
        QJniObject uri = QJniObject::callStaticObjectMethod(
2405
            "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;",
2406
            QJniObject::fromString(fileName).object<jstring>());
2407
 
482 andreas 2408
        QJniObject context = QNativeInterface::QAndroidApplication::context();
446 andreas 2409
        fileName = QJniObject::callStaticObjectMethod(
2410
            "org/qtproject/theosys/UriToPath", "getFileName",
2411
            "(Landroid/net/Uri;Landroid/content/Context;)Ljava/lang/String;",
482 andreas 2412
            uri.object(), &context).toString();
446 andreas 2413
#endif  // QT5_LINUX
2414
        if (fileName.length() > 0)
2415
            fname = fileName;
2416
    }
2417
    else
2418
    {
2419
        MSG_WARNING("Not an Uri? (" << fname.toStdString() << ")");
2420
    }
2421
#endif  // Q_OS_ANDROID
2422
 
2423
    if (gPageManager)
2424
        gPageManager->setTextToButton(handle, fname.toStdString(), true);
2425
}
2426
 
2427
void MainWindow::onTListCallbackCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
2428
{
2429
    DECL_TRACER("MainWindow::onTListCallbackCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)");
2430
 
2431
    if (!current || current == previous)
2432
        return;
2433
 
2434
    QListWidget *w = current->listWidget();
2435
    TObject::OBJECT_t *objWindow = findFirstWindow();
2436
 
2437
    while(objWindow)
2438
    {
2439
        TObject::OBJECT_t *objItem = findFirstChild(objWindow->handle);
2440
 
2441
        while (objItem)
2442
        {
2443
            if (objItem->type == TObject::OBJ_LIST && objItem->object.list == w)
2444
            {
2445
                int row = objItem->object.list->currentRow();
2446
                gPageManager->setSelectedRow(objItem->handle, row + 1, current->text().toStdString());
2447
                return;
2448
            }
2449
 
2450
            objItem = findNextChild(objItem->handle);
2451
        }
2452
 
2453
        objWindow = findNextWindow(objWindow);
2454
    }
2455
}
2456
 
2457
void MainWindow::onProgressChanged(int percent)
2458
{
2459
    DECL_TRACER("MainWindow::onProgressChanged(int percent)");
2460
 
2461
    if (!mDownloadBar || !mBusy)
2462
        return;
2463
 
2464
    mDownloadBar->setProgress(percent);
2465
}
2466
 
2467
void MainWindow::startWait(const string& text)
2468
{
2469
    DECL_TRACER("MainWindow::startWait(const string& text)");
2470
 
2471
    if (mWaitBox)
2472
    {
2473
        mWaitBox->setText(text);
2474
        return;
2475
    }
2476
 
2477
    mWaitBox = new TQtWait(this, text);
2478
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
2479
    mWaitBox->setScaleFactor(mScaleFactor);
2480
    mWaitBox->doResize();
2481
    mWaitBox->start();
2482
#endif
2483
}
2484
 
2485
void MainWindow::stopWait()
2486
{
2487
    DECL_TRACER("MainWindow::stopWait()");
2488
 
2489
    if (!mWaitBox)
2490
        return;
2491
 
2492
    mWaitBox->end();
2493
    delete mWaitBox;
2494
    mWaitBox = nullptr;
2495
}
2496
 
2497
void MainWindow::pageFinished(ulong handle)
2498
{
2499
    DECL_TRACER("MainWindow::pageFinished(uint handle)");
2500
 
2501
    TObject::OBJECT_t *obj = findObject(handle);
2502
 
2503
    if (obj)
2504
    {
2505
        if (obj->type == TObject::OBJ_SUBPAGE && (obj->animate.showEffect == SE_NONE || obj->animate.showTime <= 0) && obj->object.widget)
2506
        {
2507
            if (!obj->object.widget->isEnabled())
2508
                obj->object.widget->setEnabled(true);
2509
 
2510
            obj->object.widget->show();
2511
            obj->object.widget->lower();
2512
            obj->object.widget->raise();
2513
        }
2514
 
2515
        if (obj->type == TObject::OBJ_SUBPAGE && obj->animate.showEffect != SE_NONE && obj->object.widget)
2516
        {
2517
            if (startAnimation(obj, obj->animate))
2518
                return;
2519
        }
2520
 
2521
        if ((obj->type == TObject::OBJ_PAGE || obj->type == TObject::OBJ_SUBPAGE) && obj->object.widget)
2522
        {
2523
            QObjectList list = obj->object.widget->children();
2524
            QObjectList::Iterator iter;
2525
 
2526
            for (iter = list.begin(); iter != list.end(); ++iter)
2527
            {
2528
                QObject *o = *iter;
2529
                ulong child = extractHandle(o->objectName().toStdString());
2530
                OBJECT_t *obj = nullptr;
2531
 
2532
                if (child && (obj = findObject(child)) != nullptr)
2533
                {
2534
                    if (obj->invalid && obj->type != OBJ_SUBPAGE)
2535
                        obj->invalid = false;
2536
 
2537
                    if (obj->remove)
2538
                        obj->remove = false;
2539
 
2540
                    switch (obj->type)
2541
                    {
2542
                        case OBJ_PAGE:
2543
                        case OBJ_SUBPAGE:
2544
                            if (obj->object.widget && !obj->invalid && obj->object.widget->isHidden())
2545
                            {
2546
                                if (!obj->object.widget->isEnabled())
2547
                                    obj->object.widget->setEnabled(true);
2548
 
2549
                                obj->object.widget->show();
2550
                                obj->object.widget->lower();
2551
                                obj->object.widget->raise();
2552
                            }
2553
                        break;
2554
 
2555
                        case OBJ_BUTTON:
2556
                            if (obj->object.label && obj->object.label->isHidden())
2557
                                obj->object.label->show();
2558
                        break;
2559
 
2560
                        case OBJ_MARQUEE:
2561
                            if (obj->object.marquee && obj->object.marquee->isHidden())
2562
                                obj->object.marquee->show();
2563
                        break;
2564
 
2565
                        case OBJ_INPUT:
2566
                        case OBJ_TEXT:
2567
                            if (obj->object.plaintext && obj->object.plaintext->isHidden())
2568
                                obj->object.plaintext->show();
2569
                        break;
2570
 
2571
                        case OBJ_LIST:
2572
                            if (obj->object.list && obj->object.list->isHidden())
2573
                                obj->object.list->show();
2574
                        break;
2575
 
2576
                        case OBJ_SUBVIEW:
2577
                            if (obj->object.area && obj->object.area->isHidden())
2578
                            {
2579
                                obj->object.area->lower();
2580
                                obj->object.area->show();
2581
                                obj->object.area->raise();
2582
                            }
2583
                        break;
2584
 
2585
                        case OBJ_VIDEO:
2586
                            if (obj->object.vwidget && obj->object.vwidget->isHidden())
2587
                                obj->object.vwidget->show();
2588
                        break;
2589
 
2590
                        default:
2591
                            MSG_WARNING("Object " << handleToString(child) << " is an invalid type!");
2592
                    }
2593
                }
2594
                else
2595
                {
2596
                    QString obName = o->objectName();
2597
 
2598
                    if (obName.startsWith("Label_"))
2599
                    {
2600
                        QLabel *l = dynamic_cast<QLabel *>(o);
2601
 
2602
                        if (l->isHidden())
2603
                            l->show();
2604
                    }
2605
                }
2606
            }
2607
        }
2608
 
2609
        if (obj->type == OBJ_SUBVIEW && obj->object.area)
2610
            obj->object.area->show();
2611
#if TESTMODE == 1
2612
        __success = true;
2613
#endif
2614
    }
2615
#ifdef QT_DEBUG
2616
    else
2617
    {
2618
        MSG_WARNING("Object " << handleToString(handle) << " not found!");
2619
    }
2620
#endif
2621
#if TESTMODE == 1
2622
    setScreenDone();
2623
#endif
2624
}
2625
 
2626
/**
2627
 * @brief MainWindow::appStateChanged - Is called whenever the state of the app changes.
2628
 * This callback method is called whenever the state of the application
2629
 * changes. This is mostly usefull on mobile devices. Whenever the main window
2630
 * looses the focus (screen closed, application is put into background, ...)
2631
 * this method is called and updates a flag. If the application is not able
2632
 * to draw to the screen (suspended) all events are cached. At the moment the
2633
 * application becomes active, all queued messages are applied.
2634
 * @param state     The new state of the application.
2635
 */
2636
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
2637
{
2638
    DECL_TRACER("MainWindow::onAppStateChanged(Qt::ApplicationState state)");
2639
 
2640
    switch (state)
2641
    {
2642
        case Qt::ApplicationSuspended:              // Should not occure on a normal desktop
2643
            MSG_INFO("Switched to mode SUSPEND");
2644
            mHasFocus = false;
2645
#ifdef Q_OS_ANDROID
2646
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2647
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2648
#else
2649
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2650
#endif
2651
#endif
2652
        break;
2653
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)      // On a normal desktop we can ignore this signals
2654
        case Qt::ApplicationInactive:
2655
            MSG_INFO("Switched to mode INACTIVE");
2656
            mHasFocus = false;
2657
            mWasInactive = true;
2658
#ifdef Q_OS_ANDROID
2659
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2660
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2661
#else
2662
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2663
#endif
2664
#endif
2665
        break;
2666
 
2667
        case Qt::ApplicationHidden:
2668
            MSG_INFO("Switched to mode HIDDEN");
2669
            mHasFocus = false;
2670
#ifdef Q_OS_ANDROID
2671
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2672
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2673
#else
2674
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
2675
#endif
2676
#endif
2677
        break;
2678
#endif
2679
        case Qt::ApplicationActive:
2680
            MSG_INFO("Switched to mode ACTIVE");
2681
            mHasFocus = true;
2682
#ifdef Q_OS_IOS
2683
            initGeoLocation();
2684
#endif
2685
            if (!isRunning && gPageManager)
2686
            {
2687
                // Start the core application
2688
                gPageManager->startUp();
2689
                gPageManager->run();
2690
                isRunning = true;
2691
                mWasInactive = false;
2692
#ifdef Q_OS_IOS
2693
                // To get the battery level periodicaly we setup a timer.
2694
                if (!mIosBattery)
2695
                    mIosBattery = new TIOSBattery;
2696
 
2697
                mIosBattery->update();
2698
 
2699
                int left = mIosBattery->getBatteryLeft();
2700
                int stat = mIosBattery->getBatteryState();
2701
                MSG_DEBUG("iOS battery state: " << left << "%, State: " << stat);
2702
                // At this point no buttons are registered and therefore the battery
2703
                // state will not be visible. To have the state at the moment a button
2704
                // is registered, we tell the page manager to store the values.
2705
                gPageManager->setBattery(left, stat);
2706
                gPageManager->informBatteryStatus(left, stat);
2707
 
2708
                if (mSensor)
2709
                {
2710
                    if (mIosRotate && mOrientation == Qt::PrimaryOrientation) // Unknown?
2711
                    {
2712
                        switch(mIosRotate->getCurrentOrientation())
2713
                        {
2714
                            case O_PORTRAIT:            mOrientation = Qt::PortraitOrientation; break;
2715
                            case O_REVERSE_PORTRAIT:    mOrientation = Qt::InvertedPortraitOrientation; break;
2716
                            case O_REVERSE_LANDSCAPE:   mOrientation = Qt::InvertedLandscapeOrientation; break;
2717
                            case O_LANDSCAPE:           mOrientation = Qt::LandscapeOrientation; break;
2718
                        }
2719
                    }
2720
#if defined(QT_DEBUG) && (defined(Q_OS_IOS) || defined(Q_OS_ANDROID))
2721
                    MSG_DEBUG("Orientation after activate: " << orientationToString(mOrientation));
2722
#endif
2723
                    if (gPageManager && mIosRotate)
2724
                    {
2725
                        if (gPageManager->getSettings()->isPortrait() && mOrientation != Qt::PortraitOrientation)
2726
                        {
2727
                            mIosRotate->rotate(O_PORTRAIT);
2728
                            mOrientation = Qt::PortraitOrientation;
2729
                        }
2730
                        else if (mOrientation != Qt::LandscapeOrientation)
2731
                        {
2732
                            mIosRotate->rotate(O_LANDSCAPE);
2733
                            mOrientation = Qt::LandscapeOrientation;
2734
                        }
2735
                    }
2736
 
2737
                    setNotch();
2738
                }
2739
#endif
2740
            }
2741
            else
2742
            {
2743
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_ANDROID)
2744
                _freezeWorkaround();
2745
#endif
2746
                if (mDoRepaint || mWasInactive)
2747
                    repaintObjects();
2748
 
2749
                mDoRepaint = false;
2750
                mWasInactive = false;
2751
            }
2752
#ifdef Q_OS_ANDROID
2753
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2754
            QAndroidJniObject activity = QtAndroid::androidActivity();
2755
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/HideToolbar", "hide", "(Landroid/app/Activity;Z)V", activity.object(), true);
2756
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "resumeOrientationListener", "()V");
2757
#else
2758
            QJniObject activity = QNativeInterface::QAndroidApplication::context();
2759
//            QJniObject::callStaticMethod<void>("org/qtproject/theosys/HideToolbar", "hide", "(Landroid/app/Activity;Z)V", activity.object(), true);
2760
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "resumeOrientationListener", "()V");
2761
#endif
2762
#endif
2763
#ifdef Q_OS_IOS
2764
            // We do this to make sure the battery state is up to date after the
2765
            // screen was reactivated.
2766
            int left = mIosBattery->getBatteryLeft();
2767
            int stat = mIosBattery->getBatteryState();
2768
            gPageManager->informBatteryStatus(left, stat);
2769
 
2770
            if (mIOSSettingsActive)
2771
            {
2772
                mIOSSettingsActive = false;
2773
                MSG_DEBUG("Activating settings");
2774
                activateSettings(QASettings::getOldNetlinx(),
2775
                                  QASettings::getOldPort(),
2776
                                  QASettings::getOoldChannelID(),
2777
                                  QASettings::getOldSurface(),
2778
                                  QASettings::getOldToolbarSuppress(),
2779
                                  QASettings::getOldToolbarForce());
2780
            }
2781
#endif
2782
#if TESTMODE == 1
2783
            if (_gTestMode)
2784
                _gTestMode->run();
2785
 
2786
            _run_test_ready = true;
2787
#endif
2788
        break;
2789
#if not defined(Q_OS_IOS) && not defined(Q_OS_ANDROID)
2790
        default:
2791
            mHasFocus = true;
2792
#endif
2793
    }
2794
#if defined(Q_OS_ANDROID)
2795
    if (mHasFocus && gPageManager)
2796
    {
2797
        gPageManager->initNetworkState();
2798
        gPageManager->initBatteryState();
2799
    }
2800
    else if (gPageManager)
2801
    {
2802
        gPageManager->stopNetworkState();
2803
        gPageManager->stopBatteryState();
2804
    }
2805
#endif
2806
}
2807
 
2808
void MainWindow::_shutdown()
2809
{
2810
    DECL_TRACER("MainWindow::_shutdown()")
2811
 
2812
    close();
2813
}
2814
 
2815
/******************* Signal handling *************************/
2816
 
2817
void MainWindow::_resetSurface()
2818
{
2819
    DECL_TRACER("MainWindow::_resetSurface()");
2820
 
2821
    // Start over by exiting this class
2822
    MSG_INFO("Program will start over!");
2823
    _restart_ = true;
2824
    prg_stopped = true;
2825
    killed = true;
2826
 
2827
    if (gAmxNet)
2828
        gAmxNet->stop();
2829
 
2830
    close();
2831
}
2832
 
2833
void MainWindow::_displayButton(ulong handle, ulong parent, TBitmap buffer, int width, int height, int left, int top, bool passthrough, int marqtype, int marq)
2834
{
2835
    DECL_TRACER("MainWindow::_displayButton(ulong handle, ulong parent, TBitmap buffer, int width, int height, int left, int top, bool passthrough, int marqtype, int marq)");
2836
 
2837
    if (prg_stopped)
2838
        return;
2839
 
2840
    if (!mHasFocus)
2841
    {
2842
        markDirty(handle);
2843
        return;
2844
    }
2845
 
2846
    emit sigDisplayButton(handle, parent, buffer, width, height, left, top, passthrough, marqtype, marq);
2847
}
2848
 
2849
void MainWindow::_setMarqueeText(Button::TButton* button)
2850
{
2851
    DECL_TRACER("MainWindow::_setMarqueeText(Button::TButton* button)");
2852
 
2853
    if (prg_stopped)
2854
        return;
2855
 
2856
    emit sigSetMarqueeText(button);
2857
}
2858
 
2859
void MainWindow::_displayViewButton(ulong handle, ulong parent, bool vertical, TBitmap buffer, int width, int height, int left, int top, int space, TColor::COLOR_T fillColor)
2860
{
2861
    DECL_TRACER("MainWindow::_displayViewButton(ulong handle, ulong parent, TBitmap buffer, int width, int height, int left, int top)");
2862
 
2863
    if (prg_stopped)
2864
        return;
2865
 
2866
    if (!mHasFocus)
2867
    {
2868
        markDirty(handle);
2869
        return;
2870
    }
2871
 
2872
    emit sigDisplayViewButton(handle, parent, vertical, buffer, width, height, left, top, space, fillColor);
2873
}
2874
 
2875
void MainWindow::_addViewButtonItems(ulong parent, vector<PGSUBVIEWITEM_T> items)
2876
{
2877
    DECL_TRACER("MainWindow::_addViewButtonItems(ulong parent, vector<PGSUBVIEWITEM_T> items)");
2878
 
2879
    if (prg_stopped)
2880
        return;
2881
 
2882
    emit sigAddViewButtonItems(parent, items);
2883
}
2884
 
2885
void MainWindow::_updateViewButton(ulong handle, ulong parent, TBitmap buffer, TColor::COLOR_T fillColor)
2886
{
2887
    DECL_TRACER("MainWindow::_updateViewButton(ulong handle, ulong parent, TBitmap buffer, TColor::COLOR_T fillColor)");
2888
 
2889
    if (prg_stopped || !mHasFocus)
2890
        return;
2891
 
2892
    if (!mHasFocus)
2893
    {
2894
        markDirty(handle);
2895
        return;
2896
    }
2897
 
2898
    emit sigUpdateViewButton(handle, parent, buffer, fillColor);
2899
}
2900
 
2901
void MainWindow::_updateViewButtonItem(PGSUBVIEWITEM_T& item, ulong parent)
2902
{
2903
    DECL_TRACER("MainWindow::_updateViewButtonItem(PGSUBVIEWITEM_T& item, ulong parent)");
2904
 
2905
    if (prg_stopped || !mHasFocus)
2906
        return;
2907
 
2908
    emit sigUpdateViewButtonItem(item, parent);
2909
}
2910
 
2911
void MainWindow::_showViewButtonItem(ulong handle, ulong parent, int position, int timer)
2912
{
2913
    DECL_TRACER("MainWindow::_showViewButtonItem(ulong handle, ulong parent, int position, int timer)");
2914
 
2915
    if (prg_stopped)
2916
        return;
2917
 
2918
    if (!mHasFocus)
2919
    {
2920
        markDirty(handle);
2921
        return;
2922
    }
2923
 
2924
    emit sigShowViewButtonItem(handle, parent, position, timer);
2925
}
2926
 
2927
void MainWindow::_hideAllViewItems(ulong handle)
2928
{
2929
    DECL_TRACER("MainWindow::_hideAllViewItems(ulong handle)");
2930
 
2931
    if (prg_stopped || !mHasFocus)
2932
        return;
2933
 
2934
    emit sigHideAllViewItems(handle);
2935
}
2936
 
2937
void MainWindow::_toggleViewButtonItem(ulong handle, ulong parent, int position, int timer)
2938
{
2939
    DECL_TRACER("MainWindow::_toggleViewButtonItem(ulong handle, ulong parent, int position, int timer)");
2940
 
2941
    if (prg_stopped)
2942
        return;
2943
 
2944
    if (!mHasFocus)
2945
    {
2946
        markDirty(handle);
2947
        return;
2948
    }
2949
 
2950
    emit sigToggleViewButtonItem(handle, parent, position, timer);
2951
}
2952
 
2953
void MainWindow::_hideViewItem(ulong handle, ulong parent)
2954
{
2955
    DECL_TRACER("MainWindow::_hideViewItem(ulong handle, ulong parent)");
2956
 
2957
    if (prg_stopped || !mHasFocus)
2958
        return;
2959
 
2960
    emit sigHideViewItem(handle, parent);
2961
}
2962
 
2963
void MainWindow::_setVisible(ulong handle, bool state)
2964
{
2965
    DECL_TRACER("MainWindow::_setVisible(ulong handle, bool state)");
2966
 
2967
    if (prg_stopped || !mHasFocus)
2968
        return;
2969
 
2970
    emit sigSetVisible(handle, state);
2971
}
2972
 
2973
void MainWindow::_setSubViewPadding(ulong handle, int padding)
2974
{
2975
    DECL_TRACER("MainWindow::_setSubViewPadding(ulong handle, int padding)");
2976
 
2977
    if (prg_stopped || !mHasFocus)
2978
        return;
2979
 
2980
    emit sigSetSubViewPadding(handle, padding);
2981
}
2982
 
2983
void MainWindow::_setPage(ulong handle, int width, int height)
2984
{
2985
    DECL_TRACER("MainWindow::_setPage(ulong handle, int width, int height)");
2986
 
2987
    if (prg_stopped || !mHasFocus)
2988
        return;
2989
 
2990
    emit sigSetPage(handle, width, height);
2991
}
2992
 
2993
void MainWindow::_setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate, bool modal)
2994
{
2995
    DECL_TRACER("MainWindow::_setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate, bool modal)");
2996
 
2997
    if (prg_stopped || !mHasFocus)
2998
        return;
2999
 
3000
    emit sigSetSubPage(handle, parent, left, top, width, height, animate, modal);
3001
}
3002
 
3003
#ifdef _OPAQUE_SKIA_
3004
void MainWindow::_setBackground(ulong handle, TBitmap image, int width, int height, ulong color)
3005
#else
3006
void MainWindow::_setBackground(ulong handle, TBitmap image, int width, int height, ulong color, int opacity)
3007
#endif
3008
{
3009
    DECL_TRACER("MainWindow::_setBackground(ulong handle, TBitmap image, int width, int height, ulong color [, int opacity])");
3010
 
3011
    if (prg_stopped)
3012
        return;
3013
 
3014
    if (!mHasFocus)
3015
    {
3016
        markDirty(handle);
3017
        return;
3018
    }
3019
 
3020
#ifdef _OPAQUE_SKIA_
3021
    emit sigSetBackground(handle, image, width, height, color);
3022
#else
3023
    emit sigSetBackground(handle, image, width, height, color, opacity);
3024
#endif
3025
}
3026
 
3027
void MainWindow::_dropPage(ulong handle)
3028
{
3029
    DECL_TRACER("MainWindow::_dropPage(ulong handle)");
3030
 
3031
    if (!mHasFocus)
3032
        return;
3033
 
3034
    doReleaseButton();
3035
 
3036
    if (!mHasFocus)
3037
    {
3038
        markDroped(handle);
3039
        return;
3040
    }
3041
 
3042
    emit sigDropPage(handle);
3043
}
3044
 
3045
void MainWindow::_dropSubPage(ulong handle, ulong parent)
3046
{
3047
    DECL_TRACER("MainWindow::_dropSubPage(ulong handle, ulong parent)");
3048
 
3049
    if (!mHasFocus)
3050
        return;
3051
 
3052
    doReleaseButton();
3053
 
3054
    if (!mHasFocus)
3055
    {
3056
        markDroped(handle);
3057
        return;
3058
    }
3059
 
3060
    emit sigDropSubPage(handle, parent);
3061
}
3062
 
3063
void MainWindow::_dropButton(ulong handle)
3064
{
3065
    DECL_TRACER("MainWindow::_dropButton(ulong handle)");
3066
 
3067
    if (!mHasFocus)
3068
    {
3069
        markDroped(handle);
3070
        return;
3071
    }
3072
 
3073
    emit sigDropButton(handle);
3074
}
3075
 
3076
void MainWindow::_playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
3077
{
3078
    DECL_TRACER("MainWindow::_playVideo(ulong handle, const string& url)");
3079
 
3080
    if (prg_stopped || !mHasFocus)
3081
        return;
3082
 
3083
    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
3084
}
3085
 
3086
void MainWindow::_inputText(Button::TButton *button, Button::BITMAP_t& bm, int frame)
3087
{
3088
    DECL_TRACER("MainWindow::_inputText(Button::TButton *button, Button::BITMAP_t& bm, int frame)");
3089
 
3090
    if (prg_stopped || !button || !mHasFocus)
3091
        return;
3092
 
3093
    QByteArray buf;
3094
 
3095
    if (bm.buffer && bm.rowBytes > 0)
3096
    {
3097
        size_t size = static_cast<size_t>(bm.width) * static_cast<size_t>(bm.height) * static_cast<size_t>(bm.rowBytes / static_cast<size_t>(bm.width));
3098
        buf.insert(0, (const char *)bm.buffer, static_cast<qsizetype>(size));
3099
    }
3100
 
3101
    emit sigInputText(button, buf, bm.width, bm.height, frame, bm.rowBytes);
3102
}
3103
 
3104
void MainWindow::_listBox(Button::TButton *button, Button::BITMAP_t& bm, int frame)
3105
{
3106
    DECL_TRACER("MainWindow::_listBox(Button::TButton& button, Button::BITMAP_t& bm, int frame)");
3107
 
3108
    if (prg_stopped || !mHasFocus)
3109
        return;
3110
 
3111
    QByteArray buf;
3112
 
3113
    if (bm.buffer && bm.rowBytes > 0)
3114
    {
3115
        size_t size = static_cast<size_t>(bm.width) * static_cast<size_t>(bm.height) * static_cast<size_t>(bm.rowBytes / static_cast<size_t>(bm.width));
3116
        buf.insert(0, (const char *)bm.buffer, static_cast<qsizetype>(size));
3117
    }
3118
 
3119
    emit sigListBox(button, buf, bm.width, bm.height, frame, bm.rowBytes);
3120
}
3121
 
3122
void MainWindow::_showKeyboard(const std::string& init, const std::string& prompt, bool priv)
3123
{
3124
    DECL_TRACER("MainWindow::_showKeyboard(std::string &init, std::string &prompt, bool priv)");
3125
 
3126
    if (prg_stopped || !mHasFocus)
3127
        return;
3128
 
3129
    doReleaseButton();
3130
    emit sigKeyboard(init, prompt, priv);
3131
}
3132
 
3133
void MainWindow::_showKeypad(const std::string& init, const std::string& prompt, bool priv)
3134
{
3135
    DECL_TRACER("MainWindow::_showKeypad(std::string &init, std::string &prompt, bool priv)");
3136
 
3137
    if (prg_stopped || !mHasFocus)
3138
        return;
3139
 
3140
    doReleaseButton();
3141
    emit sigKeypad(init, prompt, priv);
3142
}
3143
 
3144
void MainWindow::_resetKeyboard()
3145
{
3146
    DECL_TRACER("MainWindow::_resetKeyboard()");
3147
 
3148
    if (mHasFocus)
3149
        emit sigResetKeyboard();
3150
}
3151
 
3152
void MainWindow::_showSetup()
3153
{
3154
    DECL_TRACER("MainWindow::_showSetup()");
3155
 
3156
    if (mHasFocus)
3157
        emit sigShowSetup();
3158
}
3159
 
3160
void MainWindow::_playSound(const string& file)
3161
{
3162
    DECL_TRACER("MainWindow::_playSound(const string& file)");
3163
 
3164
    if (mHasFocus)
3165
        emit sigPlaySound(file);
3166
}
3167
 
3168
void MainWindow::_stopSound()
3169
{
3170
    DECL_TRACER("MainWindow::_stopSound()");
3171
 
3172
    if (mHasFocus)
3173
        emit sigStopSound();
3174
}
3175
 
3176
void MainWindow::_muteSound(bool state)
3177
{
3178
    DECL_TRACER("MainWindow::_muteSound(bool state)");
3179
 
3180
    if (mHasFocus)
3181
        emit sigMuteSound(state);
3182
}
3183
 
3184
void MainWindow::_setVolume(int volume)
3185
{
3186
    DECL_TRACER("MainWindow::_setVolume(int volume)");
3187
 
3188
    if (mHasFocus)
3189
        emit sigSetVolume(volume);
3190
}
3191
 
3192
void MainWindow::_setOrientation(J_ORIENTATION ori)
3193
{
3194
#ifdef Q_OS_ANDROID
3195
    DECL_TRACER("MainWindow::_setOriantation(J_ORIENTATION ori)");
3196
 
3197
    if (ori == O_FACE_UP || ori == O_FACE_DOWN)
3198
        return;
3199
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3200
    QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
3201
#else
3202
    QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative", "activity", "()Landroid/app/Activity;");
3203
#endif
3204
    if ( activity.isValid() )
3205
    {
3206
        activity.callMethod<void>
3207
                ("setRequestedOrientation"  // method name
3208
                 , "(I)V"                   // signature
3209
                 , ori);
3210
 
3211
        switch(ori)
3212
        {
3213
            case O_LANDSCAPE:           mOrientation = Qt::LandscapeOrientation; break;
3214
            case O_PORTRAIT:            mOrientation = Qt::PortraitOrientation; break;
3215
            case O_REVERSE_LANDSCAPE:   mOrientation = Qt::InvertedLandscapeOrientation; break;
3216
            case O_REVERSE_PORTRAIT:    mOrientation = Qt::InvertedPortraitOrientation; break;
3217
            default:
3218
                MSG_WARNING("Orientation is undefined!");
3219
                mOrientation = Qt::PrimaryOrientation;
3220
        }
3221
    }
3222
#elif defined(Q_OS_IOS)
3223
    if (mIosRotate)
3224
    {
3225
        mIosRotate->rotate(ori);
3226
#ifdef QT_DEBUG
3227
        string msg;
3228
 
3229
        switch(ori)
3230
        {
3231
            case O_LANDSCAPE:           msg = "LANDSCAPE"; break;
3232
            case O_PORTRAIT:            msg = "PORTRAIT"; break;
3233
            case O_REVERSE_PORTRAIT:    msg = "INVERTED PORTRAIT"; break;
3234
            case O_REVERSE_LANDSCAPE:   msg = "INVERTED LANDSCAPE"; break;
3235
            default:
3236
                msg = "unknown";
3237
        }
3238
 
3239
        MSG_DEBUG("Rotated to " << msg);
3240
#endif
3241
    }
3242
#else
3243
    Q_UNUSED(ori);
3244
#endif
3245
}
3246
 
3247
void MainWindow::_sendVirtualKeys(const string& str)
3248
{
3249
    DECL_TRACER("MainWindow::_sendVirtualKeys(const string& str)");
3250
 
3251
    if (mHasFocus)
3252
        emit sigSendVirtualKeys(str);
3253
}
3254
 
3255
void MainWindow::_showPhoneDialog(bool state)
3256
{
3257
    DECL_TRACER("MainWindow::_showPhoneDialog(bool state)");
3258
 
3259
    if (mHasFocus)
3260
        emit sigShowPhoneDialog(state);
3261
}
3262
 
3263
void MainWindow::_setPhoneNumber(const std::string& number)
3264
{
3265
    DECL_TRACER("MainWindow::_setPhoneNumber(const std::string& number)");
3266
 
3267
    if (mHasFocus)
3268
        emit sigSetPhoneNumber(number);
3269
}
3270
 
3271
void MainWindow::_setPhoneStatus(const std::string& msg)
3272
{
3273
    DECL_TRACER("MainWindow::_setPhoneStatus(const std::string& msg)");
3274
 
3275
    if (mHasFocus)
3276
        emit sigSetPhoneStatus(msg);
3277
}
3278
 
3279
void MainWindow::_setPhoneState(int state, int id)
3280
{
3281
    DECL_TRACER("MainWindow::_setPhoneState(int state, int id)");
3282
 
3283
    if (mHasFocus)
3284
        emit sigSetPhoneState(state, id);
3285
}
3286
 
3287
void MainWindow::_onProgressChanged(int percent)
3288
{
3289
    DECL_TRACER("MainWindow::_onProgressChanged(int percent)");
3290
 
3291
    if (mHasFocus)
3292
        emit sigOnProgressChanged(percent);
3293
}
3294
 
3295
void MainWindow::_displayMessage(const string &msg, const string &title)
3296
{
3297
    DECL_TRACER("MainWindow::_displayMessage(const string &msg, const string &title)");
3298
 
3299
    if (mHasFocus)
3300
        emit sigDisplayMessage(msg, title);
3301
}
3302
 
3303
void MainWindow::_askPassword(ulong handle, const string& msg, const string& title, int x, int y)
3304
{
3305
    DECL_TRACER("MainWindow::_askPassword(ulong handle, const string& msg, const string& title, int x int y)");
3306
 
3307
    if (mHasFocus)
3308
        emit sigAskPassword(handle, msg, title, x, y);
3309
}
3310
 
3311
void MainWindow::_fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)
3312
{
3313
    DECL_TRACER("MainWindow::_fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)");
3314
 
3315
    if (!handle || path.empty())
3316
    {
3317
        MSG_WARNING("Invalid parameter handle or no path!");
3318
        return;
3319
    }
3320
 
3321
    emit sigFileDialog(handle, path, extension, suffix);
3322
}
3323
 
3324
void MainWindow::_setSizeMainWindow(int width, int height)
3325
{
3326
#if !defined (Q_OS_ANDROID) && !defined(Q_OS_IOS)
3327
    DECL_TRACER("MainWindow::_setSizeMainWindow(int width, int height)");
3328
 
3329
    emit sigSetSizeMainWindow(width, height);
3330
#else
3331
    Q_UNUSED(width);
3332
    Q_UNUSED(height);
3333
#endif
3334
}
3335
 
3336
void MainWindow::_listViewArea(ulong handle, ulong parent, Button::TButton& button, SUBVIEWLIST_T& list)
3337
{
3338
    DECL_TRACER("MainWindow::_listViewArea(ulong handle, ulong parent, Button::TButton *button, SUBVIEWLIST_T& list)");
3339
 
3340
    if (!handle || !parent || list.id <= 0)
3341
    {
3342
        MSG_WARNING("Invalid parameters for scroll area!");
3343
        return;
3344
    }
3345
 
3346
    if (!mHasFocus)
3347
    {
3348
        markDirty(handle);
3349
        return;
3350
    }
3351
 
3352
    emit sigListViewArea(handle, parent, button, list);
3353
}
3354
 
458 andreas 3355
void MainWindow::_initializeIntercom(INTERCOM_t ic)
3356
{
3357
    DECL_TRACER("MainWindow::_initializeIntercom(INTERCOM_t ic)");
3358
 
3359
    emit sigInitializeIntercom(ic);
3360
}
3361
 
3362
void MainWindow::_intercomStart()
3363
{
3364
    DECL_TRACER("MainWindow::_intercomStart()");
3365
 
3366
    emit sigIntercomStart();
3367
}
3368
 
3369
void MainWindow::_intercomStop()
3370
{
3371
    DECL_TRACER("MainWindow::_intercomStop()");
3372
 
3373
    emit sigIntercomStop();
3374
}
3375
 
3376
void MainWindow::_intercomSpkLevel(int level)
3377
{
3378
    DECL_TRACER("MainWindow::_intercomSpkLevel(int level)");
3379
 
3380
    emit sigIntercomSpkLevel(level);
3381
}
3382
 
3383
void MainWindow::_intercomMicLevel(int level)
3384
{
3385
    DECL_TRACER("MainWindow::_intercomMicLevel(int level)");
3386
 
3387
    emit sigIintercomMicLevel(level);
3388
}
3389
 
3390
void MainWindow::_intercomMicMute(bool mute)
3391
{
3392
    DECL_TRACER("MainWindow::_intercomMicMute(bool mute)");
3393
 
3394
    emit sigIntercomMicMute(mute);
3395
}
3396
 
446 andreas 3397
void MainWindow::doReleaseButton()
3398
{
3399
    DECL_TRACER("MainWindow::doReleaseButton()");
3400
 
3401
    if (mLastPressX >= 0 && mLastPressY >= 0 && gPageManager)
3402
    {
3403
        MSG_DEBUG("Sending outstanding mouse release event for coordinates x" << mLastPressX << ", y" << mLastPressY);
3404
        int x = mLastPressX;
3405
        int y = mLastPressY;
3406
 
3407
        if (isScaled())
3408
        {
3409
            x = static_cast<int>(static_cast<double>(x) / mScaleFactor);
3410
            y = static_cast<int>(static_cast<double>(y) / mScaleFactor);
3411
        }
3412
 
3413
        gPageManager->mouseEvent(x, y, false);
3414
        mLastPressX = mLastPressY = -1;
3415
    }
3416
}
3417
 
3418
/**
3419
 * @brief MainWindow::repaintObjects
3420
 * If the application was suspended, which is only on mobile devices possible,
3421
 * the surface can't be drawn. If there was a change on a visible object it
3422
 * was marked "dirty". This methos searches for all dirty marked objects and
3423
 * asks the TPageManager to resend the last drawn graphic of the object. If the
3424
 * object was a page or subpage, the whole page or subpage is redrawn. Otherwise
3425
 * only the changed object.
3426
 */
3427
void MainWindow::repaintObjects()
3428
{
3429
    DECL_TRACER("MainWindow::repaintObjects()");
3430
 
3431
    if (mRunRedraw)
3432
        return;
3433
 
3434
    std::thread thr = std::thread([=] {
3435
        mRunRedraw = true;
3436
        TObject::OBJECT_t *obj = getFirstDirty();
3437
 
3438
        while (obj)
3439
        {
3440
            if (!obj->remove && !obj->invalid && obj->dirty)
3441
            {
3442
                MSG_PROTOCOL("Refreshing widget " << handleToString (obj->handle));
3443
 
3444
                if (gPageManager)
3445
                    gPageManager->redrawObject(obj->handle);
3446
 
3447
                obj->dirty = false;
3448
            }
3449
 
3450
            obj = getNextDirty(obj);
3451
        }
3452
 
3453
        mRunRedraw = false;
3454
    });
3455
 
3456
    thr.detach();
3457
}
3458
 
3459
void MainWindow::refresh(ulong handle)
3460
{
3461
    DECL_TRACER("MainWindow::refresh(ulong handle)");
3462
 
3463
    if (!handle)
3464
        return;
3465
 
3466
    OBJECT_t *obj = findFirstChild(handle);
3467
 
3468
    while (obj)
3469
    {
3470
        MSG_DEBUG("Object " << handleToString(obj->handle) << " of type " << objectToString(obj->type) << ". Invalid: " << (obj->invalid ? "YES" : "NO") << ", Pointer: " << (obj->object.widget ? "YES" : "NO"));
3471
 
3472
        if (obj->type == OBJ_SUBVIEW && !obj->invalid && obj->object.area)
3473
        {
3474
            obj->object.area->setHidden(true);
3475
            obj->object.area->setHidden(false);
3476
            obj->object.area->setEnabled(true);
3477
            MSG_DEBUG("Subview refreshed");
3478
        }
3479
        else if (obj->type == OBJ_LIST && !obj->invalid && obj->object.list)
3480
        {
3481
            if (!obj->object.list->isEnabled())
3482
                obj->object.list->setEnabled(true);
3483
        }
3484
        else if (obj->type == OBJ_BUTTON && !obj->invalid && obj->object.label)
3485
        {
3486
            if (!obj->object.label->isEnabled())
3487
                obj->object.label->setEnabled(true);
3488
        }
3489
        else if (obj->type == OBJ_MARQUEE && !obj->invalid && obj->object.marquee)
3490
        {
3491
            if (!obj->object.marquee->isEnabled())
3492
                obj->object.marquee->setEnabled(true);
3493
        }
3494
        else if ((obj->type == OBJ_SUBPAGE || obj->type == OBJ_PAGE) && !obj->invalid && obj->object.widget)
3495
        {
3496
            if (!obj->object.widget->isEnabled())
3497
                obj->object.widget->setEnabled(true);
3498
        }
3499
 
3500
        obj = findNextChild(obj->handle);
3501
    }
3502
}
3503
 
3504
void MainWindow::markDirty(ulong handle)
3505
{
3506
    DECL_TRACER("MainWindow::markDirty(ulong handle)");
3507
 
3508
    OBJECT_t *obj = findObject(handle);
3509
 
3510
    if (!obj)
3511
        return;
3512
 
3513
    MSG_DEBUG("Object " << handleToString(handle) << " marked dirty.");
3514
    obj->dirty = true;
3515
}
3516
 
3517
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3518
int MainWindow::calcVolume(int value)
3519
#else
3520
double MainWindow::calcVolume(int value)
3521
#endif
3522
{
3523
    DECL_TRACER("MainWindow::calcVolume(int value)");
3524
 
3525
    // volumeSliderValue is in the range [0..100]
3526
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3527
    qreal linearVolume = QAudio::convertVolume(value / qreal(100.0),
3528
                                               QAudio::LogarithmicVolumeScale,
3529
                                               QAudio::LinearVolumeScale);
3530
 
3531
    return qRound(linearVolume * 100);
3532
#else
3533
    return static_cast<double>(value) / 100.0;
3534
#endif
3535
}
3536
 
3537
QFont MainWindow::loadFont(int number, const FONT_T& f, const FONT_STYLE style)
3538
{
3539
    DECL_TRACER("MainWindow::loadFont(int number, const FONT_t& f, const FONT_STYLE style)");
3540
 
3541
    QString path;
3542
    string prjPath = TConfig::getProjectPath();
3543
 
3544
    if (number < 32)    // System font?
3545
    {
3546
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
3547
        path.append(prjPath).append("/__system/graphics/fonts/").append(f.file);
3548
#else
3549
        path.append(prjPath.c_str()).append("/__system/graphics/fonts/").append(f.file.c_str());
3550
#endif
3551
 
3552
        if (!fs::is_regular_file(path.toStdString()))
3553
        {
3554
            MSG_WARNING("Seem to miss system fonts ...");
3555
            path.clear();
3556
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
3557
            path.append(prjPath).append("/fonts/").append(f.file);
3558
#else
3559
            path.append(prjPath.c_str()).append("/fonts/").append(f.file.c_str());
3560
#endif
3561
        }
3562
    }
3563
    else
3564
    {
3565
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
3566
        path.append(prjPath).append("/fonts/").append(f.file);
3567
#else
3568
        path.append(prjPath.c_str()).append("/fonts/").append(f.file.c_str());
3569
#endif
3570
        if (!fs::exists(path.toStdString()))
3571
        {
3572
            string pth = prjPath + "/__system/fonts/" + f.file;
3573
 
3574
            if (fs::exists(pth))
3575
            {
3576
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
3577
                path.assign(pth);
3578
#else
3579
                path = pth.c_str();
3580
#endif
3581
            }
3582
        }
3583
    }
3584
 
3585
    const QStringList ffamilies = QFontDatabase::families();
3586
    bool haveFont = false;
3587
    QString fname = QString::fromStdString(f.name);
3588
 
3589
    for (const QString &family : ffamilies)
3590
    {
3591
        if (family == fname)
3592
        {
3593
            haveFont = true;
3594
            break;
3595
        }
3596
    }
3597
 
3598
    // Scale the font size
3599
    int pix = f.size;
3600
 
3601
    if (mScaleFactor > 0.0 && mScaleFactor != 1.0)
3602
        pix = static_cast<int>(static_cast<double>(f.size) / mScaleFactor);
3603
 
3604
    QString qstyle;
3605
    QFont font;
3606
 
3607
    switch (style)
3608
    {
3609
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
3610
        case FONT_BOLD:         qstyle.assign("Bold"); break;
3611
        case FONT_ITALIC:       qstyle.assign("Italic"); break;
3612
        case FONT_BOLD_ITALIC:  qstyle.assign("Bold Italic"); break;
3613
 
3614
        default:
3615
            qstyle.assign("Normal");
3616
#else
3617
        case FONT_BOLD:         qstyle = "Bold"; break;
3618
        case FONT_ITALIC:       qstyle = "Italic"; break;
3619
        case FONT_BOLD_ITALIC:  qstyle = "Bold Italic"; break;
3620
 
3621
        default:
3622
            qstyle = "Normal";
3623
#endif
3624
    }
3625
 
3626
    if (!haveFont)  // Did we found the font?
3627
    {               // No, then load it
3628
        QFontDatabase::addApplicationFont(path);
3629
        font = QFontDatabase::font(fname, qstyle, pix);
3630
        MSG_DEBUG("Font \"" << path.toStdString() << "\" was loaded");
3631
    }
3632
    else
3633
    {
3634
        font.setFamily(fname);
3635
        font.setPointSize(pix);
3636
        font.setStyleName(qstyle);
3637
    }
3638
 
3639
    string family = font.family().toStdString();
3640
 
3641
    if (!font.exactMatch() && (family != f.name || font.styleName() != qstyle || font.pointSize() != pix))
3642
    {
3643
        MSG_WARNING("Using font "
3644
                    << family << "|" << font.styleName().toStdString() << "|" << font.pointSize()
3645
                    << " but requested font "
3646
                    << f.name << "|" << qstyle.toStdString() << "|" << pix << "!");
3647
    }
3648
    else
3649
    {
3650
        MSG_DEBUG("Font was set to " << f.name << "|" << qstyle.toStdString() << "|" << pix << "! " << (font.exactMatch() ? "[original]" : "[replacement]"));
3651
    }
3652
 
3653
    return font;
3654
}
3655
 
3656
/**
3657
 * @brief MainWindow::convertMask
3658
 * Converts the AMX mask for input lines into Qt mask sympols for input lines.
3659
 *
3660
 * @param mask  A string containing the AMX mask symbols.
3661
 *
3662
 * @return The converted mask string for Qt input lines.
3663
 */
3664
string MainWindow::convertMask(const string& mask)
3665
{
3666
    DECL_TRACER("MainWindow::convertMask(const string& mask)");
3667
 
3668
    string qMask;
3669
 
3670
    for (size_t i = 0; i < mask.length(); ++i)
3671
    {
3672
        switch (mask[i])
3673
        {
3674
            case '0': qMask += "9"; break;
3675
            case '9': qMask += "0"; break;
3676
            case 'A': qMask += "N"; break;
3677
            case 'a': qMask += "n"; break;
3678
            case 'L': qMask += "X"; break;
3679
            case '?': qMask += "x"; break;
3680
            case '&': qMask += "A"; break;
3681
            case 'C': qMask += "a"; break;
3682
            case '^': qMask += ";"; break;
3683
 
3684
            default:
3685
                qMask += mask[i];
3686
        }
3687
    }
3688
 
3689
    return qMask;
3690
}
3691
 
3692
#ifdef Q_OS_ANDROID
3693
void MainWindow::hideAndroidBars()
3694
{
3695
    DECL_TRACER("MainWindow::hideAndroidBars()");
3696
}
3697
#endif
3698
#ifdef Q_OS_IOS
3699
void MainWindow::setNotch()
3700
{
3701
    DECL_TRACER("MainWindow::setNotch()");
3702
 
3703
    Qt::ScreenOrientation so = getRealOrientation();
3704
 
3705
    if (so == Qt::PrimaryOrientation)
3706
        return;
3707
 
3708
    QMargins margins;
3709
 
3710
    if (mHaveNotchPortrait && (so == Qt::PortraitOrientation || so == Qt::InvertedPortraitOrientation))
3711
        margins = mNotchPortrait;
3712
    else if (mHaveNotchLandscape && (so == Qt::LandscapeOrientation || so == Qt::InvertedLandscapeOrientation))
3713
        margins = mNotchLandscape;
3714
    else
3715
    {
3716
        margins = QASettings::getNotchSize();
3717
 
3718
        if (gPageManager && gPageManager->getSettings()->isPortrait())
3719
        {
3720
            if (so == Qt::LandscapeOrientation)
3721
            {
3722
                int left = margins.left();
3723
                int top = margins.top();
3724
                margins.setTop(margins.right());
3725
                margins.setLeft(top);
3726
                margins.setRight(margins.bottom());
3727
                margins.setBottom(left);
3728
            }
3729
            else if (so == Qt::InvertedLandscapeOrientation)
3730
            {
3731
                int right = margins.right();
3732
                int top = margins.top();
3733
                margins.setTop(margins.left());
3734
                margins.setLeft(top);
3735
                margins.setRight(margins.bottom());
3736
                margins.setBottom(right);
3737
            }
3738
        }
3739
        else if (gPageManager && gPageManager->getSettings()->isLandscape())
3740
        {
3741
            if (so == Qt::PortraitOrientation)
3742
            {
3743
                int top = margins.top();
3744
                int right = margins.right();
3745
                margins.setTop(margins.left());
3746
                margins.setLeft(top);
3747
                margins.setRight(margins.bottom());
3748
                margins.setBottom(right);
3749
            }
3750
            else if (so == Qt::InvertedPortraitOrientation)
3751
            {
3752
                int top = margins.top();
3753
                int left = margins.left();
3754
                margins.setTop(margins.right());
3755
                margins.setLeft(margins.bottom());
3756
                margins.setRight(top);
3757
                margins.setBottom(left);
3758
            }
3759
        }
3760
    }
3761
#if defined(QT_DEBUG) && (defined(Q_OS_IOS) || defined(Q_OS_ANDROID))
3762
    MSG_DEBUG("Notch top: " << margins.top() << ", bottom: " << margins.bottom() << ", left: " << margins.left() << ", right: " << margins.right() << ", Orientation real: " << orientationToString(so) << ", estimated: " << orientationToString(mOrientation));
3763
#endif
3764
    if (gPageManager)
3765
    {
3766
        // If the real orientation "so" differs from "mOrientation" then
3767
        // "mOrientation" contains the wanted orientation and not the real one!
3768
        if (gPageManager->getSettings()->isPortrait() &&
3769
            (mOrientation == Qt::PortraitOrientation || mOrientation == Qt::InvertedPortraitOrientation))
3770
        {
3771
            mNotchPortrait = margins;
3772
            mHaveNotchPortrait = true;
3773
        }
3774
        else if (gPageManager->getSettings()->isLandscape() &&
3775
            (mOrientation == Qt::LandscapeOrientation || mOrientation == Qt::InvertedLandscapeOrientation))
3776
        {
3777
            mNotchLandscape = margins;
3778
            mHaveNotchLandscape = true;
3779
        }
3780
    }
3781
}
3782
 
3783
/**
3784
 * @brief MainWindow::initGeoLocation
3785
 * This method is only used on IOS to let the application run in the background.
3786
 * It is necessary because it is the only way to let an application run in
3787
 * the background.
3788
 * The method initializes the geo position module of Qt. If the app doesn't
3789
 * have the permissions to retrieve the geo positions, it will not run in the
3790
 * background. It stops at the moment the app is not in front or the display is
3791
 * closed. This makes it stop communicate with the NetLinx and looses the
3792
 * network connection. When the app gets the focus again, it must reconnect to
3793
 * the NetLinx.
3794
 */
3795
void MainWindow::initGeoLocation()
3796
{
3797
    DECL_TRACER("MainWindow::initGeoLocation()");
3798
 
3799
    if (mSource && mGeoHavePermission)
3800
        return;
3801
 
3802
    if (!mSource)
3803
    {
3804
        mGeoHavePermission = true;
3805
        mSource = QGeoPositionInfoSource::createDefaultSource(this);
3806
 
3807
        if (!mSource)
3808
        {
3809
            MSG_WARNING("Error creating geo positioning source!");
3810
            mGeoHavePermission = false;
3811
            return;
3812
        }
3813
 
3814
        mSource->setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods);
3815
        mSource->setUpdateInterval(800);    // milli seconds
3816
        // Connecting some callbacks to the class
3817
        connect(mSource, &QGeoPositionInfoSource::positionUpdated, this, &MainWindow::onPositionUpdated);
3818
        connect(mSource, &QGeoPositionInfoSource::errorOccurred, this, &MainWindow::onErrorOccurred);
3819
#ifdef Q_OS_IOS
3820
        QLocationPermission perm;
3821
        perm.setAccuracy(QLocationPermission::Approximate);
3822
        perm.setAvailability(QLocationPermission::Always);
3823
        mGeoHavePermission = false;
3824
 
3825
        switch (qApp->checkPermission(perm))
3826
        {
3827
            case Qt::PermissionStatus::Undetermined:
3828
                qApp->requestPermission(perm, [this] (const QPermission& permission)
3829
                {
3830
                    if (permission.status() == Qt::PermissionStatus::Granted)
3831
                    {
3832
                        mGeoHavePermission = true;
3833
                        mSource->startUpdates();
3834
                    }
3835
                    else
3836
                        onErrorOccurred(QGeoPositionInfoSource::AccessError);
3837
                });
3838
            break;
3839
 
3840
            case Qt::PermissionStatus::Denied:
3841
                MSG_WARNING("Location permission is denied");
3842
                onErrorOccurred(QGeoPositionInfoSource::AccessError);
3843
            break;
3844
 
3845
            case Qt::PermissionStatus::Granted:
3846
                mSource->startUpdates();
3847
                mGeoHavePermission = true;
3848
            break;
3849
        }
3850
#endif
3851
    }
3852
}
3853
 
3854
Qt::ScreenOrientation MainWindow::getRealOrientation()
3855
{
3856
    DECL_TRACER("MainWindow::getRealOrientation()");
3857
 
3858
    QScreen *screen = QGuiApplication::primaryScreen();
3859
 
3860
    if (!screen)
3861
    {
3862
        MSG_ERROR("Couldn't determine the primary screen!")
3863
        return Qt::PrimaryOrientation;
3864
    }
3865
 
3866
    QRect rect = screen->availableGeometry();
3867
 
3868
    if (rect.width() > rect.height())
3869
        return Qt::LandscapeOrientation;
3870
 
3871
    return Qt::PortraitOrientation;
3872
}
3873
#endif  // Q_OS_IOS
3874
#if defined(QT_DEBUG) && (defined(Q_OS_IOS) || defined(Q_OS_ANDROID))
3875
string MainWindow::orientationToString(Qt::ScreenOrientation ori)
3876
{
3877
    string sori;
3878
 
3879
    switch(ori)
3880
    {
3881
        case Qt::PortraitOrientation:           sori = "PORTRAIT"; break;
3882
        case Qt::InvertedPortraitOrientation:   sori = "INVERTED PORTRAIT"; break;
3883
        case Qt::LandscapeOrientation:          sori = "LANDSCAPE"; break;
3884
        case Qt::InvertedLandscapeOrientation:  sori = "INVERTED LANDSCAPE"; break;
3885
        default:
3886
            sori = "Unknown: ";
3887
            sori.append(intToString(ori));
3888
    }
3889
 
3890
    return sori;
3891
}
3892
#endif
3893
 
3894
/******************* Draw elements *************************/
3895
 
3896
/**
3897
 * @brief Displays an image.
3898
 * The method is a callback function and is called whenever an image should be
3899
 * displayed. It defines a label, set it to the (scaled) \b width and \b height
3900
 * and moves it to the (scaled) position \b left and \b top.
3901
 *
3902
 * @param handle    The unique handle of the object
3903
 * @param parent    The unique handle of the parent object.
3904
 * @param buffer    A byte array containing the image.
3905
 * @param width     The width of the object
3906
 * @param height    The height of the object
3907
 * @param pixline   The number of pixels in one line of the image.
3908
 * @param left      The prosition from the left.
3909
 * @param top       The position from the top.
3910
 * @param marqtype  The type of marquee line
3911
 * @param marq      Enabled/disabled marquee
3912
 */
3913
void MainWindow::displayButton(ulong handle, ulong parent, TBitmap buffer, int width, int height, int left, int top, bool passthrough, int marqtype, int marq)
3914
{
3915
    DECL_TRACER("MainWindow::displayButton(ulong handle, TBitmap buffer, size_t size, int width, int height, int left, int top, bool passthrough, int marqtype, int marq)");
3916
 
3917
    TObject::OBJECT_t *obj = findObject(handle);
3918
    TObject::OBJECT_t *par = findObject(parent);
3919
    MSG_TRACE("Processing button " << handleToString(handle) << " from parent " << handleToString(parent));
3920
 
3921
    if (!par)
3922
    {
3923
        if (TStreamError::checkFilter(HLOG_DEBUG))
3924
            MSG_WARNING("Button " << handleToString(handle) << " has no parent (" << handleToString(parent) << ")! Ignoring it.");
3925
#if TESTMODE == 1
3926
        setScreenDone();
3927
#endif
3928
        return;
3929
    }
3930
 
3931
    if (par->animation && !par->aniDirection)
3932
    {
3933
        if (par->animation->state() == QAbstractAnimation::Running)
3934
        {
3935
            MSG_WARNING("Object " << handleToString(parent) << " is busy with an animation!");
3936
            par->animation->stop();
3937
        }
3938
        else
3939
        {
3940
            MSG_WARNING("Object " << handleToString(parent) << " has not finished the animation!");
3941
        }
3942
 
3943
#if TESTMODE == 1
3944
        setScreenDone();
3945
#endif
3946
        return;
3947
    }
3948
    else if (par->remove)
3949
    {
3950
        MSG_WARNING("Object " << handleToString(parent) << " is marked for remove. Will not draw image!");
3951
#if TESTMODE == 1
3952
        setScreenDone();
3953
#endif
3954
        return;
3955
    }
3956
 
3957
    if (!obj)
3958
    {
3959
        if (!par->object.widget)
3960
        {
3961
            MSG_ERROR("Object " << handleToString(parent) << " has no valid widget!");
3962
#if TESTMODE == 1
3963
            setScreenDone();
3964
#endif
3965
            return;
3966
        }
3967
 
3968
        MSG_DEBUG("Adding new object " << handleToString(handle) << " ...");
3969
        OBJECT_t nobj;
3970
 
3971
        if (marqtype > 0 && marq)
3972
            nobj.type = OBJ_MARQUEE;
3973
        else
3974
            nobj.type = OBJ_BUTTON;
3975
 
3976
        nobj.handle = handle;
3977
 
449 andreas 3978
        nobj.width = scale(width);
3979
        nobj.height = scale(height);
3980
        nobj.left = scale(left);
3981
        nobj.top = scale(top);
446 andreas 3982
 
3983
        if (nobj.type == OBJ_MARQUEE)
3984
        {
3985
            nobj.object.marquee = new TQMarquee(par->object.widget, 1, static_cast<TQMarquee::MQ_TYPES>(marqtype));
3986
            nobj.object.marquee->setObjectName(QString("Marquee_") + handleToString(handle).c_str());
3987
 
3988
            if (mGestureFilter)
3989
            {
3990
                nobj.object.marquee->installEventFilter(mGestureFilter);
3991
                nobj.object.marquee->grabGesture(Qt::PinchGesture);
3992
                nobj.object.marquee->grabGesture(Qt::SwipeGesture);
3993
            }
3994
 
3995
            nobj.object.marquee->setGeometry(nobj.left, nobj.top, nobj.width, nobj.height);
3996
 
3997
            if (passthrough)
3998
                nobj.object.marquee->setAttribute(Qt::WA_TransparentForMouseEvents);
3999
        }
4000
        else
4001
        {
4002
            nobj.object.label = new QLabel(par->object.widget);
4003
            nobj.object.label->setObjectName(QString("Label_") + handleToString(handle).c_str());
4004
 
4005
            if (mGestureFilter)
4006
            {
4007
                nobj.object.label->installEventFilter(mGestureFilter);
4008
                nobj.object.label->grabGesture(Qt::PinchGesture);
4009
                nobj.object.label->grabGesture(Qt::SwipeGesture);
4010
            }
4011
 
4012
            nobj.object.label->setGeometry(nobj.left, nobj.top, nobj.width, nobj.height);
4013
 
4014
            if (passthrough)
4015
                nobj.object.label->setAttribute(Qt::WA_TransparentForMouseEvents);
4016
        }
4017
 
4018
        if (!addObject(nobj))
4019
        {
4020
            MSG_ERROR("Unable to add the new object " << handleToString(handle) << "!");
4021
#if TESTMODE == 1
4022
            setScreenDone();
4023
#endif
4024
            return;
4025
        }
4026
 
4027
        obj = findObject(handle);
4028
    }
4029
    else
4030
    {
4031
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << TObject::objectToString(obj->type) << " found!");
4032
 
4033
        if (passthrough && obj->object.label)   // Because it's a union we can test on any of the pointer types
4034
        {
4035
            if (obj->type == OBJ_BUTTON)
4036
                obj->object.label->setAttribute(Qt::WA_TransparentForMouseEvents);
4037
            else if (obj->type == OBJ_MARQUEE)
4038
                obj->object.marquee->setAttribute(Qt::WA_TransparentForMouseEvents);
4039
        }
4040
 
4041
        if (!enableObject(handle))
4042
        {
4043
            MSG_ERROR("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " couldn't be enabled!");
4044
#if TESTMODE == 1
4045
            setScreenDone();
4046
#endif
4047
            return;
4048
        }
4049
 
4050
        // In case the dimensions or position has changed we calculate the
4051
        // position and size again.
449 andreas 4052
        int wt = scale(width);
4053
        int ht = scale(height);
4054
        int lt = scale(left);
4055
        int tp = scale(top);
446 andreas 4056
 
4057
        if (obj->type != OBJ_INPUT && (wt != obj->width || ht != obj->height || lt != obj->left || tp != obj->top))
4058
        {
4059
            MSG_DEBUG("Scaled button with new size: lt: " << obj->left << ", tp: " << obj->top << ", wt: " << obj->width << ", ht: " << obj->height);
4060
 
4061
            if (obj->type == OBJ_MARQUEE)
4062
                obj->object.marquee->setGeometry(lt, tp, wt, ht);
4063
            else
4064
                obj->object.label->setGeometry(lt, tp, wt, ht);
4065
 
4066
            obj->left = lt;
4067
            obj->top = tp;
4068
            obj->width = wt;
4069
            obj->height = ht;
4070
        }
4071
    }
4072
 
4073
    if (obj->type != OBJ_INPUT)
4074
    {
4075
        try
4076
        {
4077
            if (buffer.getSize() > 0 && buffer.getPixline() > 0)
4078
            {
4079
                MSG_DEBUG("Setting image for " << handleToString(handle) << " ...");
4080
                QPixmap pixmap = scaleImage(static_cast<unsigned char *>(buffer.getBitmap()), buffer.getWidth(), buffer.getHeight(), buffer.getPixline());
4081
 
4082
                if (obj->type == OBJ_MARQUEE && obj->object.marquee)
4083
                {
4084
                    obj->object.marquee->setBackground(pixmap);
4085
#if TESTMODE == 1
4086
                    __success = true;
4087
#endif
4088
                }
4089
                else if (obj->object.label)
4090
                {
4091
                    obj->object.label->setPixmap(pixmap);
4092
#if TESTMODE == 1
4093
                    __success = true;
4094
#endif
4095
                }
4096
                else
4097
                {
4098
                    MSG_WARNING("Object " << handleToString(handle) << " does not exist any more!");
4099
                }
4100
            }
4101
        }
4102
        catch(std::exception& e)
4103
        {
4104
            MSG_ERROR("Error drawing button " << handleToString(handle) << ": " << e.what());
4105
        }
4106
        catch(...)
4107
        {
4108
            MSG_ERROR("Unexpected exception occured [MainWindow::displayButton()]");
4109
        }
4110
    }
4111
#if TESTMODE == 1
4112
    setScreenDone();
4113
#endif
4114
}
4115
 
4116
void MainWindow::setMarqueeText(Button::TButton* button)
4117
{
4118
    DECL_TRACER("MainWindow::setMarqueeText(Button::TButton* button)");
4119
 
4120
    ulong handle = button->getHandle();
4121
    TObject::OBJECT_t *obj = findObject(handle);
4122
 
4123
    if (!obj)
4124
    {
4125
        MSG_WARNING("No object " << handleToString(handle) << " found!");
4126
        return;
4127
    }
4128
 
4129
    if (obj->type != OBJ_MARQUEE || !obj->object.marquee)
4130
    {
4131
        MSG_WARNING("Object " << handleToString(handle) << " is not a Marquee type or does not exist!");
4132
        return;
4133
    }
4134
 
4135
    TQMarquee *marquee = obj->object.marquee;
479 andreas 4136
    Button::ORIENTATION to = static_cast<Button::ORIENTATION>(button->getTextJustification(nullptr, nullptr, button->getActiveInstance()));
446 andreas 4137
 
4138
    switch(to)
4139
    {
4140
        case Button::ORI_TOP_LEFT:          marquee->setAlignment(Qt::AlignTop | Qt::AlignLeft); break;
4141
        case Button::ORI_TOP_MIDDLE:        marquee->setAlignment(Qt::AlignTop | Qt::AlignHCenter); break;
4142
        case Button::ORI_TOP_RIGHT:         marquee->setAlignment(Qt::AlignTop | Qt::AlignRight); break;
4143
 
4144
        case Button::ORI_CENTER_LEFT:       marquee->setAlignment(Qt::AlignHCenter | Qt::AlignLeft); break;
4145
        case Button::ORI_CENTER_MIDDLE:     marquee->setAlignment(Qt::AlignCenter); break;
4146
        case Button::ORI_CENTER_RIGHT:      marquee->setAlignment(Qt::AlignHCenter | Qt::AlignRight); break;
4147
 
4148
        case Button::ORI_BOTTOM_LEFT:       marquee->setAlignment(Qt::AlignBottom | Qt::AlignLeft); break;
4149
        case Button::ORI_BOTTOM_MIDDLE:     marquee->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); break;
4150
        case Button::ORI_BOTTOM_RIGHT:      marquee->setAlignment(Qt::AlignBottom | Qt::AlignRight); break;
4151
 
4152
        default:
4153
            marquee->setAlignment(Qt::AlignCenter);
4154
    }
4155
 
4156
    marquee->setText(button->getText().c_str());
4157
    marquee->setSpeed(button->getMarqueeSpeed(button->getActiveInstance()));
4158
    int frameSize = scale(button->getBorderSize(button->getBorderStyle(button->getActiveInstance())));
4159
    marquee->setFrame(frameSize, frameSize, frameSize, frameSize);
4160
    QFont font = loadFont(button->getFontIndex(button->getActiveInstance()), button->getFont(), button->getFontStyle());
4161
    marquee->setFont(font);
4162
}
4163
 
4164
void MainWindow::displayViewButton(ulong handle, ulong parent, bool vertical, TBitmap buffer, int width, int height, int left, int top, int space, TColor::COLOR_T fillColor)
4165
{
4166
    DECL_TRACER("MainWindow::displayViewButton(ulong handle, TBitmap buffer, size_t size, int width, int height, int left, int top)");
4167
 
4168
    TObject::OBJECT_t *obj = findObject(handle);
4169
    TObject::OBJECT_t *par = findObject(parent);
4170
    MSG_TRACE("Processing button " << handleToString(handle) << " from parent " << handleToString(parent));
4171
 
4172
    if (!par)
4173
    {
4174
        if (TStreamError::checkFilter(HLOG_DEBUG))
4175
            MSG_WARNING("Button " << handleToString(handle) << " has no parent (" << handleToString(parent) << ")! Ignoring it.");
4176
 
4177
#if TESTMODE == 1
4178
        setScreenDone();
4179
#endif
4180
        return;
4181
    }
4182
 
4183
    if (par->animation && !par->aniDirection)
4184
    {
4185
        if (par->animation->state() == QAbstractAnimation::Running)
4186
        {
4187
            MSG_WARNING("Object " << handleToString(parent) << " is busy with an animation!");
4188
            par->animation->stop();
4189
        }
4190
        else
4191
        {
4192
            MSG_WARNING("Object " << handleToString(parent) << " has not finished the animation!");
4193
        }
4194
 
4195
#if TESTMODE == 1
4196
        setScreenDone();
4197
#endif
4198
        return;
4199
    }
4200
    else if (par->remove)
4201
    {
4202
        MSG_WARNING("Object " << handleToString(parent) << " is marked for remove. Will not draw image!");
4203
#if TESTMODE == 1
4204
        setScreenDone();
4205
#endif
4206
        return;
4207
    }
4208
 
4209
    if (!obj)
4210
    {
4211
        if (!par->object.widget)
4212
        {
4213
#if TESTMODE == 1
4214
            MSG_ERROR("Object " << handleToString(parent) << " has no valid object!");
4215
#endif
4216
            return;
4217
        }
4218
 
4219
        MSG_DEBUG("Adding new object " << handleToString(handle) << " ...");
4220
        OBJECT_t nobj;
4221
 
4222
        nobj.type = OBJ_SUBVIEW;
4223
        nobj.handle = handle;
4224
 
4225
        nobj.width = scale(width);
4226
        nobj.height = scale(height);
4227
        nobj.left = scale(left);
4228
        nobj.top = scale(top);
4229
 
4230
        nobj.object.area = new TQScrollArea(par->object.widget, nobj.width, nobj.height, vertical);
4231
        nobj.object.area->setObjectName(QString("View_") + handleToString(handle).c_str());
4232
        nobj.object.area->setScaleFactor(mScaleFactor);
4233
        nobj.object.area->setSpace(space);
4234
        nobj.object.area->move(nobj.left, nobj.top);
4235
        nobj.connected = true;
4236
        connect(nobj.object.area, &TQScrollArea::objectClicked, this, &MainWindow::onSubViewItemClicked);
4237
 
4238
        if (!addObject(nobj))
4239
        {
4240
            MSG_ERROR("Couldn't add the object " << handleToString(handle) << "!");
4241
#if TESTMODE == 1
4242
            setScreenDone();
4243
#endif
4244
            return;
4245
        }
4246
 
4247
        obj = findObject(handle);
4248
    }
4249
    else if (obj->type != OBJ_SUBVIEW)
4250
    {
4251
        MSG_ERROR("Object " << handleToString(handle) << " is of wrong type " << objectToString(obj->type) << "!");
4252
#if TESTMODE == 1
4253
        setScreenDone();
4254
#endif
4255
        return;
4256
    }
4257
    else
4258
    {
4259
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << TObject::objectToString(obj->type) << " found!");
4260
 
4261
        if (obj->object.area && !obj->connected)
4262
            reconnectArea(obj->object.area);
4263
    }
4264
 
4265
    try
4266
    {
4267
        // Set background color
4268
        if (obj->object.area)
4269
        {
4270
            QColor color;
4271
 
4272
            if (fillColor.alpha == 0)
4273
                color = Qt::transparent;
4274
            else
4275
                color = qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha);
4276
 
4277
            obj->object.area->setBackGroundColor(color);
4278
        }
4279
 
4280
        if (buffer.getSize() > 0 && buffer.getPixline() > 0)
4281
        {
4282
            MSG_DEBUG("Setting image for " << handleToString(handle) << " ...");
4283
            QPixmap pixmap = scaleImage(static_cast<unsigned char *>(buffer.getBitmap()), buffer.getWidth(), buffer.getHeight(), buffer.getPixline());
4284
 
4285
            if (pixmap.isNull())
4286
            {
4287
                MSG_ERROR("Unable to create a pixmap out of an image!");
4288
#if TESTMODE == 1
4289
                setScreenDone();
4290
#endif
4291
                return;
4292
            }
4293
 
4294
            if (obj->object.area)
4295
            {
4296
                obj->object.area->setBackgroundImage(pixmap);
4297
            }
4298
            else
4299
            {
4300
                MSG_WARNING("Object " << handleToString(handle) << " does not exist any more!");
4301
            }
4302
        }
4303
    }
4304
    catch(std::exception& e)
4305
    {
4306
        MSG_ERROR("Error drawing button " << handleToString(handle) << ": " << e.what());
4307
    }
4308
    catch(...)
4309
    {
4310
        MSG_ERROR("Unexpected exception occured [MainWindow::displayViewButton()]");
4311
    }
4312
}
4313
 
4314
void MainWindow::addViewButtonItems(ulong parent, vector<PGSUBVIEWITEM_T> items)
4315
{
4316
    DECL_TRACER("MainWindow::addViewButtonItems(ulong parent, vector<PGSUBVIEWITEM_T> items)");
4317
 
4318
    if (items.empty())
4319
        return;
4320
 
4321
    OBJECT_t *par = findObject(parent);
4322
 
4323
    if (!par || par->type != OBJ_SUBVIEW || !par->object.area)
4324
    {
4325
        MSG_ERROR("No object with handle " << handleToString(parent) << " found or object is not a subview list!");
4326
        return;
4327
    }
4328
 
4329
    if (par->invalid)
4330
    {
4331
        if (!enableObject(parent))
4332
        {
4333
            MSG_ERROR("Object " << handleToString(parent) << " of type " << objectToString(par->type) << " couldn't be enabled!");
4334
#if TESTMODE == 1
4335
            setScreenDone();
4336
#endif
4337
            return;
4338
        }
4339
    }
4340
 
4341
    if (!items.empty())
4342
    {
4343
        par->object.area->setScrollbar(items[0].scrollbar);
4344
        par->object.area->setScrollbarOffset(items[0].scrollbarOffset);
4345
        par->object.area->setAnchor(items[0].position);
4346
    }
4347
 
4348
    par->object.area->addItems(items);
4349
}
4350
 
4351
void MainWindow::updateViewButton(ulong handle, ulong parent, TBitmap buffer, TColor::COLOR_T fillColor)
4352
{
4353
    DECL_TRACER("MainWindow::updateViewButton(ulong handle, ulong parent, TBitmap buffer, TColor::COLOR_T fillColor)");
4354
 
4355
    OBJECT_t *par = findObject(parent);
4356
 
4357
    if (!par || par->type != OBJ_SUBVIEW || !par->object.area)
4358
    {
4359
        MSG_ERROR("No object with handle " << handleToString(parent) << " found for update or object is not a subview list!");
4360
#if TESTMODE == 1
4361
        setScreenDone();
4362
#endif
4363
        return;
4364
    }
4365
 
4366
    PGSUBVIEWITEM_T item;
4367
    item.handle = handle;
4368
    item.parent = parent;
4369
    item.image = buffer;
4370
    item.bgcolor = fillColor;
4371
    par->object.area->updateItem(item);
4372
}
4373
 
4374
void MainWindow::updateViewButtonItem(PGSUBVIEWITEM_T& item, ulong parent)
4375
{
4376
    DECL_TRACER("MainWindow::updateViewButtonItem(PGSUBVIEWITEM_T& item, ulong parent)");
4377
 
4378
    OBJECT_t *par = findObject(parent);
4379
 
4380
    if (!par || par->type != OBJ_SUBVIEW || !par->object.area)
4381
    {
4382
        MSG_ERROR("No object with handle " << handleToString(parent) << " found for update or object is not a subview list!");
4383
#if TESTMODE == 1
4384
        setScreenDone();
4385
#endif
4386
        return;
4387
    }
4388
 
4389
    par->object.area->updateItem(item);
4390
}
4391
 
4392
void MainWindow::showViewButtonItem(ulong handle, ulong parent, int position, int timer)
4393
{
4394
    DECL_TRACER("MainWindow::showViewButtonItem(ulong handle, ulong parent, int position, int timer)");
4395
 
4396
    Q_UNUSED(timer);
4397
 
4398
    OBJECT_t *par = findObject(parent);
4399
 
4400
    if (!par || par->type != OBJ_SUBVIEW || !par->object.area)
4401
    {
4402
        MSG_ERROR("No object with handle " << handleToString(parent) << " found for update or object is not a subview list!");
4403
#if TESTMODE == 1
4404
        setScreenDone();
4405
#endif
4406
        return;
4407
    }
4408
 
4409
    par->object.area->showItem(handle, position);
4410
}
4411
 
4412
void MainWindow::toggleViewButtonItem(ulong handle, ulong parent, int position, int timer)
4413
{
4414
    DECL_TRACER("MainWindow::toggleViewButtonItem(ulong handle, ulong parent, int position, int timer)");
4415
 
4416
    Q_UNUSED(timer);
4417
 
4418
    OBJECT_t *par = findObject(parent);
4419
 
4420
    if (!par || par->type != OBJ_SUBVIEW || !par->object.area)
4421
    {
4422
        MSG_ERROR("No object with handle " << handleToString(parent) << " found for update or object is not a subview list!");
4423
#if TESTMODE == 1
4424
        setScreenDone();
4425
#endif
4426
        return;
4427
    }
4428
 
4429
    par->object.area->showItem(handle, position);
4430
}
4431
 
4432
void MainWindow::hideAllViewItems(ulong handle)
4433
{
4434
    DECL_TRACER("MainWindow::hideAllViewItems(ulong handle)");
4435
 
4436
    OBJECT_t *obj = findObject(handle);
4437
 
4438
    if (!obj || obj->type != OBJ_SUBVIEW || !obj->object.area)
4439
    {
4440
#if TESTMODE == 1
4441
        setScreenDone();
4442
#endif
4443
        return;
4444
    }
4445
 
4446
    obj->object.area->hideAllItems();
4447
}
4448
 
4449
void MainWindow::hideViewItem(ulong handle, ulong parent)
4450
{
4451
    DECL_TRACER("MainWindow::hideViewItem(ulong handle, ulong parent)");
4452
 
4453
    OBJECT_t *obj = findObject(parent);
4454
 
4455
    if (!obj || obj->type != OBJ_SUBVIEW || !obj->object.area)
4456
    {
4457
#if TESTMODE == 1
4458
        setScreenDone();
4459
#endif
4460
        return;
4461
    }
4462
 
4463
    obj->object.area->hideItem(handle);
4464
}
4465
 
4466
void MainWindow::SetVisible(ulong handle, bool state)
4467
{
4468
    DECL_TRACER("MainWindow::SetVisible(ulong handle, bool state)");
4469
 
4470
    OBJECT_t *obj = findObject(handle);
4471
 
4472
    if (!obj)
4473
    {
4474
        MSG_ERROR("Object " << handleToString(handle) << " not found!");
4475
#if TESTMODE == 1
4476
        setScreenDone();
4477
#endif
4478
        return;
4479
    }
4480
 
4481
    if (obj->type == OBJ_BUTTON && obj->object.label)
4482
    {
4483
        obj->object.label->setVisible(state);
4484
        obj->invalid = false;
4485
        obj->remove = false;
4486
    }
4487
    else if (obj->type == OBJ_MARQUEE && obj->object.marquee)
4488
    {
4489
        obj->object.marquee->setVisible(state);
4490
        obj->invalid = false;
4491
        obj->remove = false;
4492
    }
4493
    else if (obj->type == OBJ_SUBVIEW && obj->object.area)
4494
    {
4495
        obj->object.area->setVisible(state);
4496
        obj->invalid = false;
4497
        obj->remove = false;
4498
    }
4499
    else
4500
    {
4501
        MSG_DEBUG("Ignoring non button object " << handleToString(handle));
4502
    }
4503
}
4504
 
4505
void MainWindow::setSubViewPadding(ulong handle, int padding)
4506
{
4507
    DECL_TRACER("MainWindow::setSubViewPadding(ulong handle, int padding)");
4508
 
4509
    OBJECT_t *obj = findObject(handle);
4510
 
4511
    if (!obj)
4512
    {
4513
        MSG_ERROR("Object " << handleToString(handle) << " not found!");
4514
#if TESTMODE == 1
4515
        setScreenDone();
4516
#endif
4517
        return;
4518
    }
4519
 
4520
    if (obj->type != OBJ_SUBVIEW || !obj->object.area)
4521
    {
4522
#if TESTMODE == 1
4523
        setScreenDone();
4524
#endif
4525
        return;
4526
    }
4527
 
4528
    obj->object.area->setSpace(padding);
4529
}
4530
 
4531
/**
4532
 * @brief Prepares a new object.
4533
 * The method first checks whether there exists a background widget or not. If
4534
 * not it creates a background widget with a black background. On Android this
4535
 * image is the size of the whole screen while on a desktop it is only the size
4536
 * of a page plus the task bar, if any.
4537
 * If the background image (centralWidget()) already exists, it set's only the
4538
 * credentials of the object.
4539
 * It makes sure that all child objects of the central widget are destroyed.
4540
 *
4541
 * @param handle    The handle of the new widget
4542
 * @param width     The original width of the page
4543
 * @param heoght    The original height of the page
4544
 */
4545
void MainWindow::setPage(ulong handle, int width, int height)
4546
{
4547
    DECL_TRACER("MainWindow::setPage(ulong handle, int width, int height)");
4548
 
4549
    if (handle == mActualPageHandle)
4550
    {
4551
#if TESTMODE == 1
4552
        setScreenDone();
4553
#endif
4554
        return;
4555
    }
4556
 
4557
//    TLOCKER(draw_mutex);
4558
    QWidget *wBackground = centralWidget();
4559
 
4560
    if (!wBackground)
4561
    {
4562
        MSG_ERROR("No central widget!");
4563
#if TESTMODE == 1
4564
        setScreenDone();
4565
#endif
4566
        return;
4567
    }
4568
 
4569
    if (!mCentralWidget)
4570
    {
4571
        MSG_ERROR("Stack for pages not initialized!");
4572
#if TESTMODE == 1
4573
        setScreenDone();
4574
#endif
4575
        return;
4576
    }
4577
 
4578
    // The following should be true only for the first time this method is called.
4579
    if (!mCentralInitialized)
4580
    {
4581
        QSize qs = menuBar()->sizeHint();
4582
 
449 andreas 4583
        setMinimumSize(scale(width), scale(height) + qs.height());
446 andreas 4584
        mCentralInitialized = true;
4585
    }
4586
 
4587
    OBJECT_t *obj = findObject(handle);
4588
 
4589
    if (!obj)
4590
    {
4591
        MSG_DEBUG("Adding new object " << handleToString(handle));
4592
        OBJECT_t nobj;
4593
 
4594
        nobj.handle = handle;
4595
        nobj.type = OBJ_PAGE;
4596
        nobj.object.widget = nullptr;
449 andreas 4597
        nobj.height = scale(height);
4598
        nobj.width = scale(width);
446 andreas 4599
 
4600
        if (!addObject(nobj))
4601
        {
4602
            MSG_ERROR("Error crating an object for handle " << handleToString(handle));
4603
#if TESTMODE == 1
4604
            setScreenDone();
4605
#endif
4606
            return;
4607
        }
4608
 
4609
        obj = findObject(handle);
4610
    }
4611
    else
4612
    {
4613
        if (obj->type != OBJ_PAGE)
4614
        {
4615
            MSG_WARNING("Object " << handleToString(handle) << " is not a page! Will not reuse it as a page.");
4616
#if TESTMODE == 1
4617
            setScreenDone();
4618
#endif
4619
            return;
4620
        }
4621
 
4622
        if (obj->object.widget && obj->object.widget->isHidden() && mCentralWidget->indexOf(obj->object.widget) >= 0)
4623
            obj->object.widget->setParent(wBackground);
4624
 
4625
        obj->invalid = false;
4626
        obj->remove = false;
4627
        MSG_DEBUG("Hidden object " << handleToString(handle) << " was reactivated.");
4628
    }
4629
 
4630
    if (!obj->object.widget)
4631
    {
4632
        obj->object.widget = new QWidget;
4633
        obj->object.widget->setObjectName(QString("Page_") + handleToString(handle).c_str());
4634
 
4635
        if (mGestureFilter)
4636
        {
4637
            obj->object.widget->installEventFilter(mGestureFilter);
4638
            obj->object.widget->grabGesture(Qt::PinchGesture);
4639
            obj->object.widget->grabGesture(Qt::SwipeGesture);
4640
        }
4641
 
4642
        obj->object.widget->setAutoFillBackground(true);
4643
        obj->invalid = false;
4644
        obj->object.widget->move(0, 0);
4645
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
4646
        obj->object.widget->setFixedSize(obj->width, obj->height);
4647
#else
4648
        obj->object.widget->setGeometry(geometry());
4649
#endif
4650
        mCentralWidget->addWidget(obj->object.widget);
4651
    }
4652
 
4653
    mActualPageHandle = handle;
4654
    MSG_PROTOCOL("Current page: " << handleToString(handle));
4655
}
4656
 
4657
void MainWindow::setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate, bool modal)
4658
{
4659
    DECL_TRACER("MainWindow::setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate, bool modal)");
4660
 
4661
    Q_UNUSED(height);
4662
    Q_UNUSED(modal);
4663
    OBJECT_t *par = findObject(parent);
4664
 
4665
    if (!par || par->type != OBJ_PAGE)
4666
    {
4667
        if (!par)
4668
        {
4669
            MSG_ERROR("Subpage " << handleToString(handle) << " has no parent! Ignoring it.");
4670
        }
4671
        else
4672
        {
4673
            MSG_ERROR("Subpage " << handleToString(handle) << " has invalid parent " << handleToString(parent) << " which is no page! Ignoring it.");
4674
        }
4675
#if TESTMODE == 1
4676
        setScreenDone();
4677
#endif
4678
        return;
4679
    }
4680
 
4681
    if (!par->object.widget)
4682
    {
4683
        MSG_ERROR("Parent page " << handleToString(parent) << " has no widget defined!");
4684
#if TESTMODE == 1
4685
        setScreenDone();
4686
#endif
4687
        return;
4688
    }
4689
 
4690
    if (mCentralWidget && mCentralWidget->currentWidget() != par->object.widget)
4691
    {
4692
        MSG_WARNING("The parent page " << handleToString(parent) << " is not the current page " << handleToString(handle) << "!");
4693
#if TESTMODE == 1
4694
        setScreenDone();
4695
#endif
4696
        return;
4697
    }
4698
 
4699
    OBJECT_t *obj = findObject(handle);
4700
    OBJECT_t nobj;
4701
    bool shouldAdd = false;
4702
 
4703
    if (obj && obj->type != OBJ_SUBPAGE)
4704
    {
4705
        MSG_WARNING("Object " << handleToString(handle) << " exists but is not a subpage! Refusing to create a new page with this handle.");
4706
#if TESTMODE == 1
4707
        setScreenDone();
4708
#endif
4709
        return;
4710
    }
4711
    else if (!obj)
4712
    {
4713
        obj = &nobj;
4714
        shouldAdd = true;
4715
    }
4716
    else
4717
    {
4718
        obj->invalid = false;
4719
        obj->remove = false;
4720
    }
4721
 
449 andreas 4722
    int scLeft = scale(left);
4723
    int scTop = scale(top);
4724
    int scWidth = scale(width);
4725
    int scHeight = scale(height);
446 andreas 4726
 
4727
    obj->type = OBJ_SUBPAGE;
4728
    obj->handle = handle;
4729
 
4730
    if (!obj->object.widget)
4731
    {
4732
        obj->object.widget = new QWidget(par->object.widget);
4733
        obj->object.widget->setObjectName(QString("Subpage_%1").arg(handleToString(handle).c_str()));
4734
/*
4735
        if (modal)
4736
        {
4737
            obj->object.widget->setWindowModality(Qt::WindowModal);
4738
            MSG_TRACE("Subpage " << handleToString(handle) << " is a modal page");
4739
        }
4740
*/
4741
    }
4742
    else
4743
        obj->object.widget->setParent(par->object.widget);
4744
 
4745
    obj->object.widget->setAutoFillBackground(true);
4746
    obj->object.widget->move(scLeft, scTop);
4747
    obj->object.widget->setFixedSize(scWidth, scHeight);
4748
    obj->left = scLeft;
4749
    obj->top = scTop;
4750
    obj->width = scWidth;
4751
    obj->height = scHeight;
4752
    obj->invalid = false;
4753
    obj->remove = false;
4754
    // filter move event
4755
    if (mGestureFilter)
4756
    {
4757
        obj->object.widget->installEventFilter(mGestureFilter);
4758
        obj->object.widget->grabGesture(Qt::PinchGesture);
4759
        obj->object.widget->grabGesture(Qt::SwipeGesture);
4760
    }
4761
 
4762
    obj->aniDirection = true;
4763
    obj->animate = animate;
4764
 
4765
    if (shouldAdd)
4766
    {
4767
        if (!addObject(nobj))
4768
        {
4769
            MSG_ERROR("Couldn't add the object " << handleToString(handle) << "!");
4770
            nobj.object.widget->close();
4771
#if TESTMODE == 1
4772
            setScreenDone();
4773
#endif
4774
        }
4775
    }
4776
}
4777
 
4778
#ifdef _OPAQUE_SKIA_
4779
void MainWindow::setBackground(ulong handle, TBitmap image, int width, int height, ulong color)
4780
#else
4781
void MainWindow::setBackground(ulong handle, TBitmap image, int width, int height, ulong color, int opacity)
4782
#endif
4783
{
4784
    DECL_TRACER("MainWindow::setBackground(ulong handle, TBitmap image, ulong color [, int opacity])");
4785
 
4786
    Q_UNUSED(height);
4787
 
4788
    if (!mCentralWidget)
4789
    {
4790
        MSG_ERROR("The internal page stack is not initialized!");
4791
#if TESTMODE == 1
4792
        setScreenDone();
4793
#endif
4794
        return;
4795
    }
4796
 
4797
    OBJECT_t *obj = findObject(handle);
4798
 
4799
    if (!obj || obj->remove)
4800
    {
4801
#ifdef QT_DEBUG
4802
        if (obj)
4803
        {
4804
            MSG_WARNING("No object " << handleToString(handle) << " found! (Flag remove: " << (obj->remove ? "TRUE" : "FALSE") << ")");
4805
        }
4806
        else
4807
        {
4808
            MSG_WARNING("No object " << handleToString(handle) << " found!");
4809
        }
4810
#else
4811
        MSG_WARNING("No object " << handleToString(handle) << " found!");
4812
#endif
4813
#if TESTMODE == 1
4814
        setScreenDone();
4815
#endif
4816
        return;
4817
    }
4818
    else if (obj->invalid)
4819
    {
4820
        if (!enableObject(handle))
4821
        {
4822
            MSG_ERROR("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " couldn't be anabled!");
4823
#if TESTMODE == 1
4824
            setScreenDone();
4825
#endif
4826
            return;
4827
        }
4828
    }
4829
 
4830
    if (obj->type != OBJ_SUBPAGE && obj->type != OBJ_BUTTON && obj->type != OBJ_PAGE)
4831
    {
4832
        MSG_ERROR("Method does not support object type " << objectToString(obj->type) << " for object " << handleToString(handle) << "!");
4833
#if TESTMODE == 1
4834
        setScreenDone();
4835
#endif
4836
        return;
4837
    }
4838
 
4839
    MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");
4840
 
4841
    if (obj->type == OBJ_BUTTON || obj->type == OBJ_SUBPAGE)
4842
    {
4843
        MSG_DEBUG("Processing object " << objectToString(obj->type));
4844
 
4845
        if ((obj->type == OBJ_BUTTON || obj->type == OBJ_MARQUEE) && !obj->object.label)
4846
        {
4847
            MSG_ERROR("The label of the object " << handleToString(handle) << " was not initialized!");
4848
#if TESTMODE == 1
4849
            setScreenDone();
4850
#endif
4851
            return;
4852
        }
4853
        else if (obj->type == OBJ_SUBPAGE && !obj->object.widget)
4854
        {
4855
            MSG_ERROR("The widget of the object " << handleToString(handle) << " was not initialized!");
4856
#if TESTMODE == 1
4857
            setScreenDone();
4858
#endif
4859
            return;
4860
        }
4861
 
4862
        QPixmap pix(obj->width, obj->height);
4863
 
4864
        if (TColor::getAlpha(color) == 0)
4865
            pix.fill(Qt::transparent);
4866
        else
4867
            pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));
4868
 
4869
        if (image.isValid() > 0)
4870
        {
4871
            MSG_DEBUG("Setting image of size " << image.getSize() << " (" << image.getWidth() << " x " << image.getHeight() << ")");
4872
            QImage img(image.getBitmap(), image.getWidth(), image.getHeight(), image.getPixline(), QImage::Format_ARGB32);
4873
 
449 andreas 4874
            if (isScaled())
446 andreas 4875
            {
4876
                QSize size(obj->width, obj->height);
4877
                pix.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
4878
            }
4879
            else
4880
                pix.convertFromImage(img);
4881
        }
4882
 
4883
        if (obj->type == OBJ_BUTTON)
4884
            obj->object.label->setPixmap(pix);
4885
        else if (obj->type == OBJ_MARQUEE)
4886
            obj->object.marquee->setBackground(pix);
4887
        else
4888
        {
4889
            MSG_DEBUG("Setting image as background for subpage " << handleToString(handle));
4890
            QPalette palette(obj->object.widget->palette());
4891
#ifndef _OPAQUE_SKIA_
4892
            qreal oo;
4893
 
4894
            if (opacity < 0)
4895
                oo = 0.0;
4896
            else if (opacity > 255)
4897
                oo = 1.0;
4898
            else
4899
                oo = 1.0 / 255.0 * (qreal)opacity;
4900
 
4901
            if (oo < 1.0)
4902
            {
4903
                QPixmap image(pix.size());      //Image with given size and format.
4904
                image.fill(Qt::transparent);    //fills with transparent
4905
                QPainter p(&image);
4906
                p.setOpacity(oo);               // set opacity from 0.0 to 1.0, where 0.0 is fully transparent and 1.0 is fully opaque.
4907
                p.drawPixmap(0, 0, pix);        // given pixmap into the paint device.
4908
                p.end();
4909
                palette.setBrush(QPalette::Window, QBrush(image));
4910
                MSG_DEBUG("Opacity was set to " << oo);
4911
            }
4912
            else
4913
                palette.setBrush(QPalette::Window, QBrush(pix));
4914
#else
4915
            palette.setBrush(QPalette::Window, QBrush(pix));
4916
#endif
4917
            obj->object.widget->setPalette(palette);
4918
        }
4919
    }
4920
    else if (obj->type == TObject::OBJ_PAGE)
4921
    {
4922
        MSG_DEBUG("Processing object of type PAGE ...");
4923
        QWidget *central = obj->object.widget;
4924
 
4925
        if (!central)
4926
        {
4927
            MSG_ERROR("There is no page widget initialized for page " << handleToString(handle));
4928
#if TESTMODE == 1
4929
            setScreenDone();
4930
#endif
4931
            displayMessage("Can't set a background without an active page!", "Internal error");
4932
            return;
4933
        }
4934
 
4935
        QWidget *current = mCentralWidget->currentWidget();
4936
        int index = -1;
4937
 
4938
        if (current && central != current)
4939
        {
4940
            if ((index = mCentralWidget->indexOf(central)) < 0)
4941
            {
4942
                QString obName = QString("Page_%1").arg(handleToString(handle).c_str());
4943
 
4944
                for (int i = 0; i < mCentralWidget->count(); ++i)
4945
                {
4946
                    QWidget *w = mCentralWidget->widget(i);
4947
                    MSG_DEBUG("Checking widget " << w->objectName().toStdString());
4948
 
4949
                    if (w->objectName() == obName)
4950
                    {
4951
                        index = i;
4952
                        break;
4953
                    }
4954
                }
4955
 
4956
                if (index < 0)
4957
                {
4958
                    MSG_WARNING("Missing page " << handleToString(handle) << " on stack! Will add it to the stack.");
4959
                    index = mCentralWidget->addWidget(central);
4960
                    MSG_DEBUG("Number pages on stack: " << mCentralWidget->count());
4961
                    QRect geomMain = geometry();
4962
                    QRect geomCent = mCentralWidget->geometry();
4963
                    MSG_DEBUG("Geometry MainWindow: left: " << geomMain.left() << ", right: " << geomMain.right() << ", top: " << geomMain.top() << ", bottom: " << geomMain.bottom());
4964
                    MSG_DEBUG("Geometry CentWindow: left: " << geomCent.left() << ", right: " << geomCent.right() << ", top: " << geomCent.top() << ", bottom: " << geomCent.bottom());
4965
                }
4966
            }
4967
        }
4968
        else
4969
            index = mCentralWidget->indexOf(central);
4970
 
4971
        QPixmap pix(obj->width, obj->height);
4972
        QColor backgroundColor;
4973
 
4974
        if (TColor::getAlpha(color) == 0)
4975
            backgroundColor = Qt::transparent;
4976
        else
4977
            backgroundColor = QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color)));
4978
 
4979
        pix.fill(backgroundColor);
4980
        MSG_DEBUG("Filled background of size " << pix.width() << "x" << pix.height() << " with color #" << std::setfill('0') << std::setw(8) << std::hex << color);
4981
 
4982
        if (width > 0 && image.isValid())
4983
        {
4984
            QImage img(image.getBitmap(), image.getWidth(), image.getHeight(), image.getPixline(), QImage::Format_ARGB32);
4985
            bool valid = false;
4986
 
4987
            if (!img.isNull())
4988
            {
4989
                if (isScaled())
4990
                {
4991
                    QImage bgImage = img.scaled(obj->width, obj->height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
4992
                    valid = pix.convertFromImage(bgImage);
4993
                    MSG_DEBUG("Scaled image from " << width << "x" << height << " to " << obj->width << "x" << obj->height);
4994
                }
4995
                else
4996
                {
4997
                    valid = pix.convertFromImage(img);
4998
                    MSG_DEBUG("Converted image to pixmap.");
4999
                }
5000
            }
5001
 
5002
            if (!valid || pix.isNull())
5003
            {
5004
                if (pix.isNull())
5005
                    pix = QPixmap(obj->width, obj->height);
5006
 
5007
                pix.fill(backgroundColor);
5008
                MSG_WARNING("Error converting an image! Size raw data: " << image.getSize() << ", Width: " << image.getWidth() << ", Height: " << image.getHeight() << ", Bytes per row: " << image.getPixline());
5009
            }
5010
        }
5011
 
5012
        QPalette palette(central->palette());
5013
        palette.setBrush(QPalette::Window, QBrush(pix));
5014
        central->setPalette(palette);
5015
 
5016
        if (index >= 0)
5017
            mCentralWidget->setCurrentIndex(index);
5018
        else if (mCentralWidget)
5019
        {
5020
            index = mCentralWidget->addWidget(central);
5021
            mCentralWidget->setCurrentIndex(index);
5022
            MSG_DEBUG("Page widget " << handleToString(handle) << " was added at index " << index);
5023
        }
5024
 
5025
        MSG_DEBUG("Background set");
5026
    }
5027
}
5028
 
5029
void MainWindow::disconnectArea(TQScrollArea* area)
5030
{
5031
    DECL_TRACER("MainWindow::disconnectArea(TQScrollArea* area)");
5032
 
5033
    if (!area)
5034
        return;
5035
 
5036
    disconnect(area, &TQScrollArea::objectClicked, this, &MainWindow::onSubViewItemClicked);
5037
}
5038
 
5039
void MainWindow::disconnectList(QListWidget* list)
5040
{
5041
    DECL_TRACER("MainWindow::disconnectList(QListWidget* list)");
5042
 
5043
    if (!list)
5044
        return;
5045
 
5046
    disconnect(list, &QListWidget::currentItemChanged, this, &MainWindow::onTListCallbackCurrentItemChanged);
5047
}
5048
 
5049
void MainWindow::reconnectArea(TQScrollArea * area)
5050
{
5051
    DECL_TRACER("MainWindow::reconnectArea(TQScrollArea * area)");
5052
 
5053
    if (!area)
5054
        return;
5055
 
5056
    connect(area, &TQScrollArea::objectClicked, this, &MainWindow::onSubViewItemClicked);
5057
}
5058
 
5059
void MainWindow::reconnectList(QListWidget *list)
5060
{
5061
    DECL_TRACER("MainWindow::reconnectList(QListWidget *list)");
5062
 
5063
    if (!list)
5064
        return;
5065
 
5066
    connect(list, &QListWidget::currentItemChanged, this, &MainWindow::onTListCallbackCurrentItemChanged);
5067
}
5068
 
5069
/**
5070
 * @brief MainWindow::dropPage - Marks a page invalid
5071
 * This method marks a page and all the object on the page as invalid. They are
5072
 * not deleted. Instead the page, a QWidget representing a page, is set hidden.
5073
 * With it all childs of the QWidget are also hidden. So the page can be
5074
 * displayed later when it is used again.
5075
 *
5076
 * @param handle    The handle of the page.
5077
 */
5078
void MainWindow::dropPage(ulong handle)
5079
{
5080
//    TLOCKER(draw_mutex);
5081
    DECL_TRACER("MainWindow::dropPage(ulong handle)");
5082
 
5083
    MSG_PROTOCOL("Dropping page " << handleToString(handle));
5084
 
5085
    OBJECT_t *obj = findObject(handle);
5086
 
5087
    if (!obj)
5088
    {
5089
        MSG_WARNING("Object " << handleToString(handle) << " (Page) does not exist. Ignoring!");
5090
#if TESTMODE == 1
5091
        setScreenDone();
5092
#endif
5093
        return;
5094
    }
5095
 
5096
    if (obj->type != OBJ_PAGE)
5097
    {
5098
        MSG_WARNING("Object " << handleToString(handle) << " is not a page!");
5099
#if TESTMODE == 1
5100
        setScreenDone();
5101
#endif
5102
        return;
5103
    }
5104
 
5105
    MSG_DEBUG("Dropping page " << handleToString(handle));
5106
    invalidateAllSubObjects(handle);
5107
 
5108
    if (obj->object.widget)
5109
    {
5110
        obj->object.widget->setHidden(true);
5111
#if TESTMODE == 1
5112
        __success = true;
5113
#endif
5114
    }
5115
#if TESTMODE == 1
5116
    setScreenDone();
5117
#endif
5118
}
5119
 
5120
void MainWindow::dropSubPage(ulong handle, ulong parent)
5121
{
5122
    DECL_TRACER("MainWindow::dropSubPage(ulong handle, ulong parent)");
5123
 
5124
    OBJECT_t *obj = findObject(handle);
5125
    OBJECT_t *par = findObject(parent);
5126
 
5127
    if (!obj)
5128
    {
5129
        MSG_WARNING("Object " << handleToString(handle) << " (SubPage) does not exist. Ignoring!");
5130
#if TESTMODE == 1
5131
        setScreenDone();
5132
#endif
5133
        return;
5134
    }
5135
 
5136
    if (!par)
5137
    {
5138
        MSG_DEBUG("Parent object " << handleToString(parent) << " not found!");
5139
#if TESTMODE == 1
5140
        setScreenDone();
5141
#endif
5142
        return;
5143
    }
5144
 
5145
    if (obj->type != OBJ_SUBPAGE)
5146
    {
5147
        MSG_WARNING("Object " << handleToString(handle) << " is not a SubPage!");
5148
#if TESTMODE == 1
5149
        setScreenDone();
5150
#endif
5151
        return;
5152
    }
5153
 
5154
    QWidget *w = par->object.widget->findChild<QWidget *>(QString("Subpage_%1").arg(handleToString(handle).c_str()));
5155
 
5156
    if (!w)
5157
    {
5158
        MSG_DEBUG("Parent object " << handleToString(parent) << " has no child " << handleToString(handle) << "!");
5159
        obj->object.widget = nullptr;
5160
        obj->remove = true;
5161
        obj->invalid = true;
5162
#if TESTMODE == 1
5163
        setScreenDone();
5164
#endif
5165
        return;
5166
    }
5167
 
5168
    MSG_DEBUG("Dropping subpage " << handleToString(handle));
5169
    invalidateAllSubObjects(handle);
5170
    obj->aniDirection = false;
5171
    bool ret = startAnimation(obj, obj->animate, false);
5172
 
5173
    if (obj->animate.hideEffect == SE_NONE || !ret || !mLastObject)
5174
    {
5175
        obj->invalid = true;
5176
        obj->remove = false;
5177
 
5178
        if (obj->object.widget)
5179
        {
5180
            obj->object.widget->hide();
5181
#if TESTMODE == 1
5182
            __success = true;
5183
#endif
5184
        }
5185
    }
5186
#if TESTMODE == 1
5187
    setScreenDone();
5188
#endif
5189
}
5190
 
5191
void MainWindow::dropButton(ulong handle)
5192
{
5193
//    TLOCKER(draw_mutex);
5194
    DECL_TRACER("MainWindow::dropButton(ulong handle)");
5195
 
5196
    OBJECT_t *obj = findObject(handle);
5197
 
5198
    if (!obj)
5199
    {
5200
        MSG_WARNING("Object " << handleToString(handle) << " does not exist. Ignoring!");
5201
        return;
5202
    }
5203
 
5204
    if (obj->type == OBJ_PAGE || obj->type == OBJ_SUBPAGE)
5205
        return;
5206
 
5207
    invalidateObject(handle);
5208
}
5209
 
5210
void MainWindow::setSizeMainWindow(int width, int height)
5211
{
5212
#if !defined (Q_OS_ANDROID) && !defined(Q_OS_IOS)
5213
    DECL_TRACER("MainWindow::setSizeMainWindow(int width, int height)");
5214
 
5215
    QRect geo = geometry();
5216
    setGeometry(geo.x(), geo.y(), width, height + menuBar()->height());
5217
#else
5218
    Q_UNUSED(width);
5219
    Q_UNUSED(height);
5220
#endif
5221
}
5222
 
5223
void MainWindow::playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
5224
{
5225
//    TLOCKER(draw_mutex);
5226
    DECL_TRACER("MainWindow::playVideo(ulong handle, const string& url, const string& user, const string& pw))");
5227
 
5228
    TObject::OBJECT_t *obj = findObject(handle);
5229
    TObject::OBJECT_t *par = findObject(parent);
5230
    MSG_TRACE("Processing button " << handleToString(handle) << " from parent " << handleToString(parent));
5231
 
5232
    if (!par)
5233
    {
5234
        MSG_WARNING("Button has no parent! Ignoring it.");
5235
        return;
5236
    }
5237
 
5238
    if (!obj)
5239
    {
5240
        MSG_DEBUG("Adding new video object ...");
5241
        OBJECT_t nobj;
5242
 
5243
        nobj.type = TObject::OBJ_VIDEO;
5244
        nobj.handle = handle;
449 andreas 5245
        nobj.width = scale(width);
5246
        nobj.height = scale(height);
5247
        nobj.left = scale(left);
5248
        nobj.top = scale(top);
446 andreas 5249
 
5250
        nobj.object.vwidget = new QVideoWidget(par->object.widget);
5251
        nobj.object.vwidget->installEventFilter(this);
5252
 
5253
        if (!addObject(nobj))
5254
        {
5255
            MSG_ERROR("Error creating a video object!");
5256
            return;
5257
        }
5258
 
5259
        obj = findObject(handle);
5260
    }
5261
    else
5262
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");
5263
 
5264
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5265
    QMediaPlaylist *playlist = new QMediaPlaylist;
5266
#endif
5267
    QUrl qurl(url.c_str());
5268
 
5269
    if (!user.empty())
5270
        qurl.setUserName(user.c_str());
5271
 
5272
    if (!pw.empty())
5273
        qurl.setPassword(pw.c_str());
5274
 
5275
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5276
    playlist->addMedia(qurl);
5277
#endif
5278
    obj->player = new QMediaPlayer;
5279
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5280
    obj->player->setPlaylist(playlist);
5281
#else
5282
    obj->player->setSource(qurl);
5283
#endif
5284
    obj->player->setVideoOutput(obj->object.vwidget);
5285
 
5286
    obj->object.vwidget->show();
5287
    obj->player->play();
5288
}
5289
 
5290
void MainWindow::inputText(Button::TButton *button, QByteArray buf, int width, int height, int frame, size_t pixline)
5291
{
5292
    DECL_TRACER("MainWindow::inputText(Button::TButton& button, QByteArray buf, int width, int height, int frame, size_t pixline)");
5293
 
5294
    if (!button)
5295
    {
5296
        MSG_WARNING("Method was called with no button!");
5297
        return;
5298
    }
5299
 
5300
    ulong handle = button->getHandle();
5301
    ulong parent = button->getParent();
5302
    TObject::OBJECT_t *obj = findObject(handle);
5303
    TObject::OBJECT_t *par = findObject(parent);
5304
    MSG_TRACE("Processing button " << handleToString(handle) << " from parent " << handleToString(parent) << " with frame width " << frame);
5305
 
5306
    if (!par)
5307
    {
5308
        MSG_WARNING("Button has no parent! Ignoring it.");
5309
        return;
5310
    }
5311
 
5312
    int instance = button->getActiveInstance();
5313
    MSG_DEBUG("Instance: " << instance);
5314
 
5315
    if (!obj)
5316
    {
5317
        MSG_DEBUG("Adding new input object ...");
5318
        OBJECT_t nobj;
5319
 
5320
        nobj.type = TObject::OBJ_INPUT;
5321
        nobj.handle = handle;
449 andreas 5322
        nobj.width = scale(width);
5323
        nobj.height = scale(height);
5324
        nobj.left = scale(button->getLeftPosition());
5325
        nobj.top = scale(button->getTopPosition());
446 andreas 5326
 
5327
        string text = button->getText(0);
5328
        string placeholder = button->getText(1);
5329
        string mask = button->getInputMask();
5330
 
5331
        if (button->isMultiLine())
5332
            text = ReplaceString(text, "|", "\n");
5333
 
5334
        nobj.object.plaintext = new TQEditLine(text, par->object.widget, button->isMultiLine());
5335
        nobj.object.plaintext->setObjectName(string("EditLine_") + handleToString(handle));
5336
        nobj.object.plaintext->setHandle(handle);
5337
        nobj.object.plaintext->move(nobj.left, nobj.top);
5338
        nobj.object.plaintext->setFixedSize(nobj.width, nobj.height);
5339
        nobj.object.plaintext->setPadding(frame, frame, frame, frame);
5340
        nobj.object.plaintext->setPasswordChar(button->getPasswordChar());
5341
        nobj.wid = nobj.object.plaintext->winId();
5342
 
5343
        if (!placeholder.empty())
5344
            nobj.object.plaintext->setPlaceholderText(placeholder);
5345
 
5346
        bool sys = false;
5347
 
5348
        if (button->getAddressPort() == 0 || button->getChannelPort() == 0)
5349
        {
5350
            int ch = 0;
5351
 
5352
            if (button->getAddressPort() == 0 && button->getAddressChannel() > 0)
5353
                ch = button->getAddressChannel();
5354
            else if (button->getChannelPort() == 0 && button->getChannelNumber() > 0)
5355
                ch = button->getChannelNumber();
5356
 
5357
            switch(ch)
5358
            {
5359
                case SYSTEM_ITEM_SIPPORT:
5360
                case SYSTEM_ITEM_NETLINX_PORT:
5361
                    nobj.object.plaintext->setInputMask("000000");
5362
                    nobj.object.plaintext->setNumericInput();
5363
                    sys = true;
5364
                break;
5365
 
5366
                case SYSTEM_ITEM_NETLINX_CHANNEL:
5367
                    nobj.object.plaintext->setInputMask("99999");
5368
                    nobj.object.plaintext->setNumericInput();
5369
                    sys = true;
5370
                break;
5371
            }
5372
 
5373
            if (sys)
5374
                MSG_TRACE("System button " << ch << " detected.");
5375
        }
5376
 
5377
        if (!sys && !mask.empty())
5378
            nobj.object.plaintext->setInputMask(convertMask(mask));
5379
 
5380
        if (!buf.size() || pixline == 0)
5381
        {
5382
            MSG_ERROR("No image!");
5383
            TError::setError();
5384
            return;
5385
        }
5386
 
5387
        MSG_DEBUG("Background image size: " << width << " x " << height << ", rowBytes: " << pixline);
5388
        QPixmap pix(width, height);
5389
        QImage img((uchar *)buf.data(), width, height, QImage::Format_ARGB32);
5390
 
449 andreas 5391
        if (isScaled())
446 andreas 5392
            pix.convertFromImage(img.scaled(scale(width), scale(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
5393
        else
5394
            pix.convertFromImage(img);
5395
 
5396
        nobj.object.plaintext->setBackgroundPixmap(pix);
5397
        // Load the font
5398
        QFont font = loadFont(button->getFontIndex(button->getActiveInstance()), button->getFont(), button->getFontStyle());
5399
        QPalette palette(this->palette());
5400
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor(instance));
5401
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor(instance));
5402
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
5403
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
5404
        palette.setColor(QPalette::Window, cfcolor);
5405
        palette.setColor(QPalette::Base, cfcolor);
5406
        palette.setColor(QPalette::Text, txcolor);
5407
 
5408
        nobj.object.plaintext->setFont(font);
5409
        nobj.object.plaintext->setPalette(palette);
5410
        nobj.object.plaintext->setTextColor(txcolor);
5411
 
5412
        if (!addObject(nobj))
5413
        {
5414
            MSG_ERROR("Error creating an input object!");
5415
            return;
5416
        }
5417
 
5418
        connect(nobj.object.plaintext, &TQEditLine::inputChanged, this, &MainWindow::onInputChanged);
5419
        connect(nobj.object.plaintext, &TQEditLine::cursorPositionChanged, this, &MainWindow::onCursorChanged);
5420
        connect(nobj.object.plaintext, &TQEditLine::focusChanged, this, &MainWindow::onFocusChanged);
5421
    }
5422
    else
5423
    {
5424
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");
5425
 
5426
        string text = button->getText(0);
5427
        string placeholder = button->getText(1);
5428
        string mask = button->getInputMask();
5429
        MSG_DEBUG("Setting text: \"" << text << "\" with mask: \"" << mask << "\"");
5430
 
5431
        if (!placeholder.empty())
5432
            obj->object.plaintext->setPlaceholderText(placeholder);
5433
 
5434
        if (button->isMultiLine())
5435
            text = ReplaceString(text, "|", "\n");
5436
 
5437
        obj->object.plaintext->setText(text);
5438
 
5439
        if (!mask.empty())
5440
            obj->object.plaintext->setInputMask(convertMask(mask));
5441
 
5442
        QPixmap pix(obj->width, obj->height);
5443
        QImage img((uchar *)buf.data(), width, height, QImage::Format_ARGB32);
5444
 
5445
        if (isScaled())
5446
            pix.convertFromImage(img.scaled(obj->width, obj->height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
5447
        else
5448
            pix.convertFromImage(img);
5449
 
5450
//        obj->object.plaintext->setBgPixmap(pix);
5451
        obj->object.plaintext->setBackgroundPixmap(pix);
5452
        QPalette palette(this->palette());
5453
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor(instance));
5454
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor(instance));
5455
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
5456
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
5457
        palette.setColor(QPalette::Window, cfcolor);
5458
        palette.setColor(QPalette::Base, cfcolor);
5459
        palette.setColor(QPalette::Text, txcolor);
5460
//        palette.setBrush(QPalette::Window, QBrush(pix));
5461
 
5462
        obj->object.plaintext->setPalette(palette);
5463
        obj->object.plaintext->setTextColor(txcolor);
5464
    }
5465
}
5466
 
5467
void MainWindow::listBox(Button::TButton *button, QByteArray buffer, int width, int height, int frame, size_t pixline)
5468
{
5469
    DECL_TRACER("MainWindow::listBox(Button::TButton& button, QByteArray buffer, int width, int height, int frame, size_t pixline)");
5470
 
5471
    ulong handle = button->getHandle();
5472
    ulong parent = button->getParent();
5473
    TObject::OBJECT_t *obj = findObject(handle);
5474
    TObject::OBJECT_t *par = findObject(parent);
5475
    MSG_TRACE("Processing list " << handleToString(handle) << " from parent " << handleToString(parent) << " with frame width " << frame);
5476
 
5477
    if (!par)
5478
    {
5479
        MSG_WARNING("List has no parent! Ignoring it.");
5480
        return;
5481
    }
5482
 
5483
    if (!obj)
5484
    {
5485
        MSG_DEBUG("Adding new list object ...");
5486
        OBJECT_t nobj;
5487
 
5488
        nobj.type = TObject::OBJ_LIST;
5489
        nobj.handle = handle;
5490
        nobj.rows = button->getListNumRows();
5491
        nobj.cols = button->getListNumCols();
449 andreas 5492
        nobj.width = scale(width);
5493
        nobj.height = scale(height);
5494
        nobj.left = scale(button->getLeftPosition());
5495
        nobj.top = scale(button->getTopPosition());
446 andreas 5496
 
5497
        vector<string> listContent = button->getListContent();
5498
 
5499
        if (par->type == TObject::OBJ_PAGE)
5500
            nobj.object.list = new QListWidget(par->object.widget ? par->object.widget : centralWidget());
5501
        else
5502
            nobj.object.list = new QListWidget(par->object.widget);
5503
 
5504
        nobj.object.list->move(nobj.left, nobj.top);
5505
        nobj.object.list->setFixedSize(nobj.width, nobj.height);
5506
        connect(nobj.object.list, &QListWidget::currentItemChanged, this, &MainWindow::onTListCallbackCurrentItemChanged);
5507
 
5508
        if (!buffer.size() || pixline == 0)
5509
        {
5510
            MSG_ERROR("No image!");
5511
            TError::setError();
5512
            return;
5513
        }
5514
 
5515
        MSG_DEBUG("Background image size: " << width << " x " << height << ", rowBytes: " << pixline);
5516
        QPixmap pix(width, height);
5517
        QImage img((uchar *)buffer.data(), width, height, QImage::Format_ARGB32);
5518
 
449 andreas 5519
        if (isScaled())
446 andreas 5520
            pix.convertFromImage(img.scaled(scale(width), scale(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
5521
        else
5522
            pix.convertFromImage(img);
5523
 
5524
        // Load the font
5525
        FONT_T font = button->getFont();
5526
        vector<string> fontList = TFont::getFontPathList();
5527
        vector<string>::iterator iter;
5528
        string ffile;
5529
 
5530
        for (iter = fontList.begin(); iter != fontList.end(); ++iter)
5531
        {
5532
            TValidateFile vf;
5533
 
5534
            if (!vf.isValidFile(*iter + "/" + font.file))
5535
                continue;
5536
 
5537
            ffile = *iter + "/" + font.file;
5538
            break;
5539
        }
5540
 
5541
        if (ffile.empty())
5542
        {
5543
            MSG_ERROR("Font " << font.file << " doesn't exists!");
5544
            return;
5545
        }
5546
 
5547
        if (QFontDatabase::addApplicationFont(ffile.c_str()) == -1)
5548
        {
5549
            MSG_ERROR("Font " << ffile << " could not be loaded!");
5550
            TError::setError();
5551
            return;
5552
        }
5553
 
5554
        QFont ft;
5555
        ft.setFamily(font.name.c_str());
449 andreas 5556
        ft.setPointSize(font.size);
446 andreas 5557
 
5558
        MSG_DEBUG("Using font \"" << font.name << "\" with size " << font.size << "pt.");
5559
 
5560
        switch (button->getFontStyle())
5561
        {
5562
            case FONT_BOLD:     ft.setBold(true); break;
5563
            case FONT_ITALIC:   ft.setItalic(true); break;
5564
            case FONT_BOLD_ITALIC:
5565
                ft.setBold(true);
5566
                ft.setItalic(true);
5567
            break;
5568
 
5569
            default:
5570
                ft.setBold(false);
5571
                ft.setItalic(false);
5572
        }
5573
 
5574
        QPalette palette;
5575
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor());
5576
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor());
5577
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
5578
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
5579
        palette.setColor(QPalette::Base, cfcolor);
5580
        palette.setColor(QPalette::Text, txcolor);
5581
//        pix.save("frame.png");
5582
        palette.setBrush(QPalette::Base, QBrush(pix));
5583
 
5584
        nobj.object.list->setFont(ft);
5585
        nobj.object.list->setPalette(palette);
5586
        // Add content
5587
        if (!listContent.empty())
5588
        {
5589
            vector<string>::iterator iter;
5590
 
5591
            if (nobj.object.list->count() > 0)
5592
                nobj.object.list->clear();
5593
 
5594
            MSG_DEBUG("Adding " << listContent.size() << " entries to list.");
5595
            string selected = gPageManager->getSelectedItem(handle);
5596
            int index = 0;
5597
 
5598
            for (iter = listContent.begin(); iter != listContent.end(); ++iter)
5599
            {
5600
                nobj.object.list->addItem(iter->c_str());
5601
 
5602
                if (selected == *iter)
5603
                    nobj.object.list->setCurrentRow(index);
5604
 
5605
                index++;
5606
            }
5607
        }
5608
        else
5609
            MSG_DEBUG("No items for list!");
5610
 
5611
//        nobj.object.list->show();
5612
 
5613
        if (!addObject(nobj))
5614
        {
5615
            MSG_ERROR("Error creating a list object!");
5616
            return;
5617
        }
5618
    }
5619
    else
5620
    {
5621
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");
5622
        enableObject(handle);
5623
    }
5624
}
5625
 
5626
void MainWindow::showKeyboard(const std::string& init, const std::string& prompt, bool priv)
5627
{
5628
    DECL_TRACER("MainWindow::showKeyboard(std::string &init, std::string &prompt, bool priv)");
5629
 
5630
    if (mKeyboard)
5631
        return;
5632
 
5633
    mQKeyboard = new TQKeyboard(init, prompt, this);
5634
    mKeyboard = true;
5635
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
5636
    mQKeyboard->setScaleFactor(mScaleFactor);
5637
#endif
5638
    mQKeyboard->setPrivate(priv);
5639
    mQKeyboard->doResize();
5640
    mQKeyboard->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
5641
    int ret = mQKeyboard->exec();
5642
    string text = "KEYB-";
5643
 
5644
    if (ret == QDialog::Accepted)
5645
        text.append(mQKeyboard->getText());
5646
    else
5647
        text = "KEYB-ABORT";
5648
 
5649
    if (gPageManager)
5650
        gPageManager->sendKeyboard(text);
5651
 
5652
    delete mQKeyboard;
5653
    mQKeyboard = nullptr;
5654
    mKeyboard = false;
5655
}
5656
 
5657
void MainWindow::showKeypad(const std::string& init, const std::string& prompt, bool priv)
5658
{
5659
    DECL_TRACER("MainWindow::showKeypad(std::string& init, std::string& prompt, bool priv)");
5660
 
5661
    if (mKeypad)
5662
        return;
5663
 
5664
    mQKeypad = new TQKeypad(init, prompt, this);
5665
    mKeypad = true;
5666
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
5667
    mQKeypad->setScaleFactor(mScaleFactor);
5668
#endif
5669
    mQKeypad->setPrivate(priv);
5670
    mQKeypad->setMaxLength(50);     // Standard maximum length
5671
    mQKeypad->doResize();
5672
    mQKeypad->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
5673
    int ret = mQKeypad->exec();
5674
 
5675
    if (ret == QDialog::Accepted)
5676
    {
5677
        string text = "KEYP-";
5678
        text.append(mQKeypad->getText());
5679
 
5680
        if (gPageManager)
5681
            gPageManager->sendKeypad(text);
5682
    }
5683
    else
5684
    {
5685
        string text = "KEYP-ABORT";
5686
 
5687
        if (gPageManager)
5688
            gPageManager->sendKeypad(text);
5689
    }
5690
 
5691
    delete mQKeypad;
5692
    mQKeypad = nullptr;
5693
    mKeypad = false;
5694
}
5695
 
5696
void MainWindow::resetKeyboard()
5697
{
5698
    DECL_TRACER("MainWindow::resetKeyboard()");
5699
 
5700
    if (mQKeyboard)
5701
        mQKeyboard->reject();
5702
 
5703
    if (mQKeypad)
5704
        mQKeypad->reject();
5705
}
5706
 
5707
void MainWindow::sendVirtualKeys(const string& str)
5708
{
5709
    DECL_TRACER("MainWindow::sendVirtualKeys(const string& str)");
5710
 
5711
    if (mKeyboard && mQKeyboard)
5712
        mQKeyboard->setString(str);
5713
    else if (mKeypad && mQKeypad)
5714
        mQKeypad->setString(str);
5715
}
5716
 
5717
void MainWindow::showSetup()
5718
{
5719
    DECL_TRACER("MainWindow::showSetup()");
5720
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
5721
    settings();
5722
#else
5723
#ifndef Q_OS_IOS
5724
    if (gPageManager)
5725
        gPageManager->showSetup();
5726
#else
5727
    mIOSSettingsActive = true;
5728
    QASettings::openSettings();
5729
#endif  // Q_OS_IOS
5730
#endif  // !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
5731
}
5732
 
5733
void MainWindow::playSound(const string& file)
5734
{
5735
    DECL_TRACER("MainWindow::playSound(const string& file)");
5736
 
5737
    MSG_DEBUG("Playing file " << file);
5738
 
5739
    if (TConfig::getMuteState())
5740
    {
5741
#if TESTMODE == 1
5742
        __success = true;
5743
        setAllDone();
5744
#endif
5745
        return;
5746
    }
5747
 
5748
    if (!mMediaPlayer)
5749
    {
5750
        mMediaPlayer = new QMediaPlayer;
5751
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
5752
        mAudioOutput = new QAudioOutput;
5753
        mMediaPlayer->setAudioOutput(mAudioOutput);
5754
#endif
5755
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
5756
        connect(mMediaPlayer, &QMediaPlayer::playingChanged, this, &MainWindow::onPlayingChanged);
5757
#endif
5758
        connect(mMediaPlayer, &QMediaPlayer::mediaStatusChanged, this, &MainWindow::onMediaStatusChanged);
5759
        connect(mMediaPlayer, &QMediaPlayer::errorOccurred, this, &MainWindow::onPlayerError);
5760
    }
5761
 
5762
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5763
    mMediaPlayer->setMedia(QUrl::fromLocalFile(file.c_str()));
5764
    mMediaPlayer->setVolume(calcVolume(TConfig::getSystemVolume()));
5765
 
5766
    if (mMediaPlayer->state() != QMediaPlayer::StoppedState)
5767
        mMediaPlayer->stop();
5768
#else   // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5769
    mMediaPlayer->setSource(QUrl::fromLocalFile(file.c_str()));
5770
    mAudioOutput->setVolume(static_cast<float>(calcVolume(TConfig::getSystemVolume())));
5771
 
5772
    if (!mMediaPlayer->isAvailable())
5773
    {
5774
        MSG_WARNING("No audio modul found!");
5775
#if TESTMODE == 1
5776
        setAllDone();
5777
#endif
5778
        return;
5779
    }
5780
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
5781
    if (mMediaPlayer->isPlaying())
5782
        mMediaPlayer->setPosition(0);
5783
//        mMediaPlayer->stop();
5784
#else
5785
    if (mMediaPlayer->playbackState() != QMediaPlayer::StoppedState)
5786
        mMediaPlayer->stop();
5787
 
5788
    mMediaPlayer->setPosition(0);
5789
#endif  // #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
5790
 
5791
#endif  // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5792
    mMediaPlayer->play();
5793
#if TESTMODE == 1
5794
    if (mMediaPlayer->error() != QMediaPlayer::NoError)
5795
    {
5796
        MSG_ERROR("Error playing \"" << file << "\": " << mMediaPlayer->errorString().toStdString());
5797
    }
5798
    else
5799
        __success = true;
5800
 
5801
    setAllDone();
5802
#endif
5803
}
5804
 
5805
void MainWindow::stopSound()
5806
{
5807
    DECL_TRACER("MainWindow::stopSound()");
5808
 
5809
    if (mMediaPlayer)
5810
        mMediaPlayer->stop();
5811
}
5812
 
5813
void MainWindow::muteSound(bool state)
5814
{
5815
    DECL_TRACER("MainWindow::muteSound(bool state)");
5816
 
5817
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5818
    if (mMediaPlayer)
5819
        mMediaPlayer->setMuted(state);
5820
#else
5821
    if (mAudioOutput)
5822
        mAudioOutput->setMuted(state);
5823
#endif
5824
#if TESTMODE == 1
5825
    __success = true;
5826
    setAllDone();
5827
#endif
5828
}
5829
 
5830
void MainWindow::setVolume(int volume)
5831
{
5832
    DECL_TRACER("MainWindow::setVolume(int volume)");
5833
 
5834
    if (!mMediaPlayer)
5835
    {
5836
#if TESTMODE == 1
5837
        setAllDone();
5838
#endif
5839
        return;
5840
    }
5841
 
5842
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5843
    mMediaPlayer->setVolume(calcVolume(volume));
5844
#else   // QT_VERSION
5845
    if (!mMediaPlayer || !mAudioOutput)
5846
    {
5847
#if TESTMODE == 1
5848
        setAllDone();
5849
#endif  // TESTMODE
5850
        return;
5851
    }
5852
 
5853
    mAudioOutput->setVolume(static_cast<float>(calcVolume(volume)));
5854
#if TESTMODE == 1
5855
    __success = true;
5856
    setAllDone();
5857
#endif  // TESTMODE
5858
#endif  // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5859
}
5860
 
5861
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
5862
void MainWindow::onPlayingChanged(bool playing)
5863
{
5864
    DECL_TRACER("MainWindow::onPlayingChanged(bool playing)");
5865
 
5866
    // If playing stopped for whatever reason, we rewind the track
5867
    if (!playing && mMediaPlayer)
5868
    {
5869
        mMediaPlayer->setPosition(0);
5870
        MSG_DEBUG("Track was rewound.");
5871
    }
5872
}
5873
#endif
5874
 
5875
void MainWindow::onMediaStatusChanged(QMediaPlayer::MediaStatus status)
5876
{
5877
    DECL_TRACER("MainWindow::onMediaStatusChanged(QMediaPlayer::MediaStatus status)");
5878
 
5879
    switch(status)
5880
    {
461 andreas 5881
        case QMediaPlayer::NoMedia:         MSG_WARNING("There is no current media."); break;
446 andreas 5882
        case QMediaPlayer::LoadingMedia:    MSG_INFO("The current media is being loaded."); break;
5883
        case QMediaPlayer::LoadedMedia:     MSG_INFO("The current media has been loaded."); break;
5884
        case QMediaPlayer::StalledMedia:    MSG_WARNING("Playback of the current media has stalled due to insufficient buffering or some other temporary interruption."); break;
5885
        case QMediaPlayer::BufferingMedia:  MSG_INFO("The player is buffering data but has enough data buffered for playback to continue for the immediate future."); break;
5886
        case QMediaPlayer::BufferedMedia:   MSG_INFO("The player has fully buffered the current media."); break;
5887
        case QMediaPlayer::EndOfMedia:      MSG_INFO("Playback has reached the end of the current media."); break;
5888
        case QMediaPlayer::InvalidMedia:    MSG_WARNING("The current media cannot be played.");
5889
    }
5890
}
5891
 
5892
void MainWindow::onPlayerError(QMediaPlayer::Error error, const QString &errorString)
5893
{
5894
    DECL_TRACER("MainWindow::onPlayerError(QMediaPlayer::Error error, const QString &errorString)");
5895
 
5896
    if (error == QMediaPlayer::NoError)
5897
        return;
5898
 
5899
    MSG_ERROR("Media player error (" << error << "): " << errorString.toStdString());
5900
}
458 andreas 5901
 
5902
void MainWindow::initializeIntercom(INTERCOM_t ic)
5903
{
5904
    DECL_TRACER("MainWindow::initializeIntercom(INTERCOM_t ic)");
5905
 
5906
    mIntercom.setIntercom(ic);
5907
}
5908
 
5909
void MainWindow::intercomStart()
5910
{
5911
    DECL_TRACER("MainWindow::intercomStart()");
5912
 
5913
    mIntercom.start();
5914
}
5915
 
5916
void MainWindow::intercomStop()
5917
{
5918
    DECL_TRACER("MainWindow::intercomStop()");
5919
 
5920
    mIntercom.stop();
5921
}
5922
 
5923
void MainWindow::intercomMicLevel(int level)
5924
{
5925
    DECL_TRACER("MainWindow::intercomMicLevel(int level)");
5926
 
461 andreas 5927
    mIntercom.setMicrophoneLevel(level);
458 andreas 5928
}
5929
 
5930
void MainWindow::intercomSpkLevel(int level)
5931
{
5932
    DECL_TRACER("MainWindow::intercomSpkLevel(int level)");
5933
 
5934
    mIntercom.setSpeakerLevel(level);
5935
}
5936
 
5937
void MainWindow::intercomMicMute(bool mute)
5938
{
5939
    DECL_TRACER("MainWindow::intercomMicMute(bool mute)");
5940
 
5941
    mIntercom.setMute(mute);
5942
}
5943
 
446 andreas 5944
/*
5945
void MainWindow::playShowList()
5946
{
5947
    DECL_TRACER("MainWindow::playShowList()");
5948
 
5949
    _EMIT_TYPE_t etype = getNextType();
5950
 
5951
    while (etype != ET_NONE)
5952
    {
5953
        ulong handle = 0;
5954
        ulong parent = 0;
5955
        int left = 0;
5956
        int top = 0;
5957
        int width = 0;
5958
        int height = 0;
5959
        int frame = 0;
5960
        TBitmap image;
5961
        ulong color = 0;
5962
        int space{0};
5963
        bool vertical = false;
5964
        TColor::COLOR_T fillColor;
5965
        bool pth = false;
5966
#ifndef _OPAQUE_SKIA_
5967
        int opacity = 255;
5968
#endif
5969
        ANIMATION_t animate;
5970
        std::string url;
5971
        std::string user;
5972
        std::string pw;
5973
        Button::TButton *button;
5974
        Button::BITMAP_t bm;
5975
 
5976
        switch(etype)
5977
        {
5978
            case ET_BACKGROUND:
5979
#ifdef _OPAQUE_SKIA_
5980
                if (getBackground(&handle, &image, &width, &height, &color))
5981
#else
5982
                if (getBackground(&handle, &image, &width, &height, &color, &opacity))
5983
#endif
5984
                {
5985
                    MSG_PROTOCOL("Replay: BACKGROUND of object " << handleToString(handle));
5986
#ifdef _OPAQUE_SKIA_
5987
                    emit sigSetBackground(handle, image, width, height, color);
5988
#else
5989
                    emit sigSetBackground(handle, image, width, height, color, opacity);
5990
#endif
5991
                }
5992
            break;
5993
 
5994
            case ET_BUTTON:
5995
                if (getButton(&handle, &parent, &image, &left, &top, &width, &height, &pth))
5996
                {
5997
                    MSG_PROTOCOL("Replay: BUTTON object " << handleToString(handle));
5998
                    emit sigDisplayButton(handle, parent, image, width, height, left, top, pth);
5999
                }
6000
            break;
6001
 
6002
            case ET_INTEXT:
6003
                if (getInText(&handle, &button, &bm, &frame))
6004
                {
6005
                    QByteArray buf;
6006
 
6007
                    if (bm.buffer && bm.rowBytes > 0)
6008
                    {
6009
                        size_t s = (size_t)bm.width * (size_t)bm.height * (size_t)(bm.rowBytes / bm.width);
6010
                        buf.insert(0, (const char *)bm.buffer, s);
6011
                    }
6012
 
6013
                    MSG_PROTOCOL("Replay: INTEXT object " << handleToString(handle));
6014
                    emit sigInputText(button, buf, bm.width, bm.height, (int)bm.rowBytes, frame);
6015
                }
6016
            break;
6017
 
6018
            case ET_PAGE:
6019
                if (getPage(&handle, &width, &height))
6020
                {
6021
                    MSG_PROTOCOL("Replay: PAGE object " << handleToString(handle));
6022
 
6023
                    if (isDeleted())
6024
                        emit sigDropPage(handle);
6025
                    else
6026
                        emit sigSetPage(handle, width, height);
6027
                }
6028
            break;
6029
 
6030
            case ET_SUBPAGE:
6031
                if (getSubPage(&handle, &parent, &left, &top, &width, &height, &animate))
6032
                {
6033
                    MSG_PROTOCOL("Replay: SUBPAGE object " << handleToString(handle));
6034
 
6035
                    if (isDeleted())
6036
                        emit sigDropSubPage(handle, parent);
6037
                    else
6038
                        emit sigSetSubPage(handle, parent, left, top, width, height, animate);
6039
                }
6040
            break;
6041
 
6042
            case ET_SUBVIEW:
6043
                if (getViewButton(&handle, &parent, &vertical, &image, &left, &top, &width, &height, &space, &fillColor))
6044
                {
6045
                    MSG_PROTOCOL("Replay: SUBVIEW object " << handleToString(handle));
6046
                    emit sigDisplayViewButton(handle, parent, vertical, image, width, height, left, top, space, fillColor);
6047
                }
6048
            break;
6049
 
6050
            case ET_VIDEO:
6051
                if (getVideo(&handle, &parent, &left, &top, &width, &height, &url, &user, &pw))
6052
                {
6053
                    MSG_PROTOCOL("Replay: VIDEO object " << handleToString(handle));
6054
                    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
6055
                }
6056
            break;
6057
 
6058
            default:
6059
                MSG_WARNING("Type " << etype << " is currently not supported!");
6060
        }
6061
 
6062
        dropType(etype);
6063
        etype = getNextType();
6064
    }
6065
}
6066
*/
6067
 
6068
int MainWindow::scale(int value)
6069
{
6070
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
6071
    double s = gScale;
6072
#else
6073
    double s = mScaleFactor;
6074
#endif
6075
    if (value <= 0 || s == 1.0 || s < 0.0)
6076
        return value;
6077
 
6078
    return static_cast<int>(static_cast<double>(value) * s);
6079
}
6080
 
6081
bool MainWindow::isScaled()
6082
{
6083
    DECL_TRACER("MainWindow::isScaled()");
6084
 
6085
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
6086
    double s = gScale;
6087
#else
6088
    double s = mScaleFactor;
6089
#endif
6090
    if (s > 0.0 && s != 1.0 && gPageManager && TConfig::getScale())
6091
        return true;
6092
 
6093
    return false;
6094
}
6095
 
6096
bool MainWindow::startAnimation(TObject::OBJECT_t* obj, ANIMATION_t& ani, bool in)
6097
{
6098
    DECL_TRACER("MainWindow::startAnimation(OBJECT_t* obj, ANIMATION_t& ani)");
6099
 
6100
    if (!obj)
6101
    {
6102
        MSG_ERROR("Got no object to start the animation!");
6103
        return false;
6104
    }
6105
 
6106
//    TLOCKER(anim_mutex);
6107
    int scLeft = obj->left;
6108
    int scTop = obj->top;
6109
    int scWidth = obj->width;
6110
    int scHeight = obj->height;
6111
    int duration = (in ? ani.showTime : ani.hideTime);
6112
    SHOWEFFECT_t effect = (in ? ani.showEffect : ani.hideEffect);
6113
    mLastObject = nullptr;
6114
 
6115
    if (effect == SE_NONE || duration <= 0 || (obj->type != OBJ_SUBPAGE && obj->type != OBJ_PAGE))
6116
        return false;
6117
 
6118
    if (!obj->object.widget)
6119
    {
6120
        MSG_WARNING("Object " << handleToString(obj->handle) << " has no widget defined! Ignoring fade effect.");
6121
#if TESTMODE == 1
6122
        setScreenDone();
6123
#endif
6124
        return false;
6125
    }
6126
 
6127
    if (effect == SE_FADE)
6128
    {
6129
        MSG_DEBUG("Fading object " << handleToString(obj->handle) << (in ? " IN" : " OUT"));
6130
        QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(obj->object.widget);
6131
 
6132
        if (effect)
6133
        {
6134
            obj->object.widget->setGraphicsEffect(effect);
6135
            obj->animation = new QPropertyAnimation(effect, "opacity");
6136
        }
6137
    }
6138
    else
6139
    {
6140
        MSG_DEBUG("Moving object " << handleToString(obj->handle) << (in ? " IN" : " OUT"));
6141
        obj->animation = new QPropertyAnimation(obj->object.widget);
6142
        obj->animation->setTargetObject(obj->object.widget);
6143
    }
6144
 
6145
    obj->animation->setDuration(duration * 100);    // convert 10th of seconds into milliseconds
6146
    MSG_DEBUG("Processing animation effect " << effect << " with a duration of " << obj->animation->duration() << "ms");
6147
 
6148
    switch(effect)
6149
    {
6150
        case SE_SLIDE_BOTTOM_FADE:
6151
        case SE_SLIDE_BOTTOM:
6152
            obj->animation->setPropertyName("geometry");
6153
 
6154
            if (in)
6155
            {
6156
                obj->animation->setStartValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
6157
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
6158
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
6159
                obj->object.widget->show();
6160
            }
6161
            else
6162
            {
6163
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
6164
                obj->animation->setEndValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
6165
                obj->remove = true;
6166
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
6167
            }
6168
 
6169
            mLastObject = obj;
6170
            mAnimObjects.insert(pair<ulong, OBJECT_t *>(obj->handle, obj));
6171
            obj->animation->start();
6172
            MSG_DEBUG("Animation SLIDE BOTTOM started.");
6173
        break;
6174
 
6175
        case SE_SLIDE_LEFT_FADE:
6176
        case SE_SLIDE_LEFT:
6177
            obj->animation->setPropertyName("geometry");
6178
 
6179
            if (in)
6180
            {
6181
                obj->animation->setStartValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
6182
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
6183
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
6184
                obj->object.widget->show();
6185
            }
6186
            else
6187
            {
6188
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
6189
                obj->animation->setEndValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
6190
                obj->remove = true;
6191
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
6192
            }
6193
 
6194
            mLastObject = obj;
6195
            mAnimObjects.insert(pair<ulong, OBJECT_t *>(obj->handle, obj));
6196
            obj->animation->start();
6197
        break;
6198
 
6199
        case SE_SLIDE_RIGHT_FADE:
6200
        case SE_SLIDE_RIGHT:
6201
            obj->animation->setPropertyName("geometry");
6202
 
6203
            if (in)
6204
            {
6205
                obj->animation->setStartValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
6206
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
6207
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
6208
                obj->object.widget->show();
6209
            }
6210
            else
6211
            {
6212
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
6213
                obj->animation->setEndValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
6214
                obj->remove = true;
6215
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
6216
            }
6217
 
6218
            mLastObject = obj;
6219
            mAnimObjects.insert(pair<ulong, OBJECT_t *>(obj->handle, obj));
6220
            obj->animation->start();
6221
        break;
6222
 
6223
        case SE_SLIDE_TOP_FADE:
6224
        case SE_SLIDE_TOP:
6225
            obj->animation->setPropertyName("geometry");
6226
 
6227
            if (in)
6228
            {
6229
                obj->animation->setStartValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
6230
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
6231
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
6232
                obj->object.widget->show();
6233
            }
6234
            else
6235
            {
6236
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
6237
                obj->animation->setEndValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
6238
                obj->remove = true;
6239
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
6240
            }
6241
 
6242
            mLastObject = obj;
6243
            mAnimObjects.insert(pair<ulong, OBJECT_t *>(obj->handle, obj));
6244
            obj->animation->start();
6245
        break;
6246
 
6247
        case SE_FADE:
6248
            if (in)
6249
            {
6250
                if (obj->object.widget)
6251
                {
6252
                    obj->object.widget->setWindowOpacity(0.0);
6253
                    obj->object.widget->show();
6254
                }
6255
 
6256
                obj->animation->setStartValue(0.0);
6257
                obj->animation->setEndValue(1.0);
6258
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationInFinished);
6259
                obj->object.widget->show();
6260
            }
6261
            else
6262
            {
6263
                obj->animation->setStartValue(1.0);
6264
                obj->animation->setEndValue(0.0);
6265
                obj->remove = true;
6266
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
6267
            }
6268
 
6269
            mLastObject = obj;
6270
            mAnimObjects.insert(pair<ulong, OBJECT_t *>(obj->handle, obj));
6271
            obj->animation->setEasingCurve(QEasingCurve::Linear);
6272
            obj->animation->start();
6273
        break;
6274
 
6275
        default:
6276
            MSG_WARNING("Subpage effect " << ani.showEffect << " is not supported.");
6277
            delete obj->animation;
6278
            obj->animation = nullptr;
6279
            return false;
6280
    }
6281
 
6282
    return true;
6283
}
6284
 
6285
void MainWindow::downloadBar(const string &msg, QWidget *parent)
6286
{
6287
    DECL_TRACER("void MainWindow::downloadBar(const string &msg, QWidget *parent)");
6288
 
6289
    if (mBusy)
6290
        return;
6291
 
6292
    mBusy = true;
6293
    mDownloadBar = new TqDownload(msg, parent);
6294
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
6295
    mDownloadBar->setScaleFactor(gScale);
6296
    mDownloadBar->doResize();
6297
#endif
6298
    mDownloadBar->show();
6299
}
6300
 
6301
void MainWindow::runEvents()
6302
{
6303
    DECL_TRACER("MainWindow::runEvents()");
6304
 
6305
    QApplication::processEvents();
6306
}
6307
 
6308
void MainWindow::onSubViewItemClicked(ulong handle, bool pressed)
6309
{
6310
    DECL_TRACER("MainWindow::onSubViewItemClicked(ulong handle)");
6311
 
6312
    if (!handle)
6313
        return;
6314
 
6315
    // Create a thread and call the base program function.
6316
    // We create a thread to not interrupt the QT framework longer then
6317
    // necessary.
6318
    if (gPageManager)
6319
        gPageManager->mouseEvent(handle, pressed);
6320
}
6321
 
6322
void MainWindow::onInputChanged(ulong handle, string& text)
6323
{
6324
    DECL_TRACER("MainWindow::onInputChanged(ulong handle, string& text)");
6325
 
6326
    try
6327
    {
6328
        std::thread thr = std::thread([=] {
6329
            if (gPageManager)
6330
                gPageManager->inputButtonFinished(handle, text);
6331
        });
6332
 
6333
        thr.detach();
6334
    }
6335
    catch (std::exception& e)
6336
    {
6337
        MSG_ERROR("Error starting a thread to handle input line finish: " << e.what());
6338
    }
6339
}
6340
 
6341
void MainWindow::onFocusChanged(ulong handle, bool in)
6342
{
6343
    DECL_TRACER("MainWindow::onFocusChanged(ulong handle, bool in)");
6344
 
6345
    try
6346
    {
6347
        std::thread thr = std::thread([=] {
6348
            if (gPageManager)
6349
                gPageManager->inputFocusChanged(handle, in);
6350
        });
6351
 
6352
        thr.detach();
6353
    }
6354
    catch (std::exception& e)
6355
    {
6356
        MSG_ERROR("Error starting a thread to handle input line finish: " << e.what());
6357
    }
6358
}
6359
 
6360
void MainWindow::onCursorChanged(ulong handle, int oldPos, int newPos)
6361
{
6362
    DECL_TRACER("MainWindow::onCursorChanged(ulong handle, int oldPos, int newPos)");
6363
 
6364
    try
6365
    {
6366
        std::thread thr = std::thread([=] {
6367
            if (gPageManager)
6368
                gPageManager->inputCursorPositionChanged(handle, oldPos, newPos);
6369
        });
6370
 
6371
        thr.detach();
6372
    }
6373
    catch (std::exception& e)
6374
    {
6375
        MSG_ERROR("Error starting a thread to handle input line finish: " << e.what());
6376
    }
6377
}
6378
 
6379
void MainWindow::onGestureEvent(QObject *obj, QGestureEvent *event)
6380
{
6381
    DECL_TRACER("MainWindow::onGestureEvent(QObject *obj, QGestureEvent *event)");
6382
 
6383
    Q_UNUSED(obj);
6384
    gestureEvent(event);
6385
}
6386
 
6387
QPixmap MainWindow::scaleImage(QPixmap& pix)
6388
{
6389
    DECL_TRACER("MainWindow::scaleImage(QPixmap& pix)");
6390
 
6391
    int width = scale(pix.width());
6392
    int height = scale(pix.height());
6393
 
6394
    return pix.scaled(width, height);
6395
}
6396
 
6397
QPixmap MainWindow::scaleImage(unsigned char* buffer, int width, int height, int pixline)
6398
{
6399
    DECL_TRACER("MainWindow::scaleImage(unsigned char* buffer, int width, int height, int pixline)");
6400
 
6401
    if (!buffer || width < 1 || height < 1 || pixline < (width * 4))
6402
    {
6403
        MSG_ERROR("Invalid image for scaling!");
6404
        return QPixmap();
6405
    }
6406
 
6407
    QImage img(buffer, width, height, pixline, QImage::Format_ARGB32);  // Original size
6408
 
6409
    if (img.isNull() || !img.valid(width-1, height-1))
6410
    {
6411
        MSG_ERROR("Unable to create a valid image!");
6412
        return QPixmap();
6413
    }
6414
 
6415
    QSize size(scale(width), scale(height));
6416
    QPixmap pixmap;
6417
    bool ret = false;
6418
 
6419
    if (isScaled())
6420
        ret = pixmap.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Scaled size
6421
    else
6422
        ret = pixmap.convertFromImage(img);
6423
 
6424
    if (!ret || pixmap.isNull())
6425
    {
6426
        MSG_ERROR("Unable to create a pixmap out of an image!");
6427
    }
6428
 
6429
    return pixmap;
6430
}
6431
 
6432
#ifndef QT_NO_SESSIONMANAGER
6433
void MainWindow::commitData(QSessionManager &manager)
6434
{
6435
    if (manager.allowsInteraction())
6436
    {
6437
        if (!settingsChanged)
6438
            manager.cancel();
6439
    }
6440
    else
6441
    {
6442
        if (settingsChanged)
6443
            writeSettings();
6444
    }
6445
}
6446
#endif