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