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