Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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