Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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