Subversion Repositories tpanel

Rev

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