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