Subversion Repositories tpanel

Rev

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