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