Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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