Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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