Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
181 andreas 1
 
2 andreas 2
/*
101 andreas 3
 * Copyright (C) 2020 to 2022 by Andreas Theofilu <andreas@theosys.at>
2 andreas 4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software Foundation,
17
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18
 */
19
 
21 andreas 20
/** @file tqtmain.cpp
21
 * @brief Implements the surface of the application.
22
 *
23
 * This file implements the callback functions of the suface. While the most
24
 * classes are drawing the elements, the methods here take the ready elements
25
 * and display them. This file makes the surface completely independent of
26
 * the rest of the application which makes it easy to change the surface by
27
 * any other technology.
28
 */
2 andreas 29
#include <QApplication>
23 andreas 30
#include <QGuiApplication>
14 andreas 31
#include <QByteArray>
2 andreas 32
#include <QCommandLineParser>
33
#include <QCommandLineOption>
9 andreas 34
#include <QLabel>
2 andreas 35
#include <QtWidgets>
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>
181 andreas 45
#ifdef QT5_LINUX
46
#   include <QtMultimedia/QMediaPlaylist>
47
#else
48
#   include <QAudioOutput>
49
#endif
205 andreas 50
#include <QListWidget>
24 andreas 51
#include <QLayout>
40 andreas 52
#include <QSizePolicy>
21 andreas 53
#include <QUrl>
13 andreas 54
#include <QThread>
181 andreas 55
#ifdef QT5_LINUX
56
#   include <QSound>
57
#   include <QtSensors/QOrientationSensor>
217 andreas 58
#   include <qpa/qplatformscreen.h>
187 andreas 59
#else
188 andreas 60
#   include <QPlainTextEdit>
217 andreas 61
#   include <QtSensors/QOrientationReading>
181 andreas 62
#endif
182 andreas 63
#if defined(Q_OS_ANDROID) && defined(QT5_LINUX)
183 andreas 64
#include <QtAndroidExtras/QAndroidJniObject>
211 andreas 65
#include <QtAndroidExtras/QtAndroid>
88 andreas 66
#endif
5 andreas 67
#include <functional>
14 andreas 68
#include <mutex>
23 andreas 69
#ifdef __ANDROID__
70
#include <android/log.h>
71
#endif
4 andreas 72
#include "tpagemanager.h"
2 andreas 73
#include "tqtmain.h"
74
#include "tconfig.h"
251 andreas 75
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
2 andreas 76
#include "tqtsettings.h"
213 andreas 77
#endif
62 andreas 78
#include "tqkeyboard.h"
79
#include "tqkeypad.h"
5 andreas 80
#include "tcolor.h"
92 andreas 81
#include "texcept.h"
118 andreas 82
#include "ttpinit.h"
179 andreas 83
#include "tqdownload.h"
140 andreas 84
#include "tqtphone.h"
190 andreas 85
#include "tqeditline.h"
211 andreas 86
#include "terror.h"
243 andreas 87
#ifdef Q_OS_IOS
250 andreas 88
#include "ios/QASettings.h"
89
#include "ios/tiosrotate.h"
90
#include "ios/tiosbattery.h"
243 andreas 91
#endif
2 andreas 92
 
209 andreas 93
#if __cplusplus < 201402L
94
#   error "This module requires at least C++14 standard!"
95
#else
96
#   if __cplusplus < 201703L
97
#       include <experimental/filesystem>
98
namespace fs = std::experimental::filesystem;
99
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
100
#   else
101
#       include <filesystem>
102
#       ifdef __ANDROID__
103
namespace fs = std::__fs::filesystem;
104
#       else
105
namespace fs = std::filesystem;
106
#       endif
107
#   endif
108
#endif
109
 
21 andreas 110
/**
44 andreas 111
 * @def THREAD_WAIT
112
 * This defines a time in milliseconds. It is used in the callbacks for the
113
 * functions to draw the elements. Qt need a little time to do it's work.
114
 * Therefore we're waiting a little bit. Otherwise it may result in missing
115
 * elements or black areas on the screen.
21 andreas 116
 */
100 andreas 117
#define THREAD_WAIT     1
44 andreas 118
 
119
extern amx::TAmxNet *gAmxNet;                   //!< Pointer to the class running the thread which handles the communication with the controller.
90 andreas 120
extern bool _restart_;                          //!< If this is set to true then the whole program will start over.
92 andreas 121
extern TPageManager *gPageManager;              //!< The pointer to the global defined main class.
21 andreas 122
std::mutex draw_mutex;                          //!< We're using threads and need to block execution sometimes
38 andreas 123
static bool isRunning = false;                  //!< TRUE = the pageManager was started.
107 andreas 124
TObject *gObject = nullptr;                     //!< Internal used pointer to a TObject class. (Necessary because of threads!)
100 andreas 125
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
44 andreas 126
static double gScale = 1.0;                     //!< Global variable holding the scale factor.
127
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 128
std::atomic<double> mScaleFactorW{1.0};
129
std::atomic<double> mScaleFactorH{1.0};
130
int gScreenWidth{0};
131
int gScreenHeight{0};
100 andreas 132
#endif
21 andreas 133
 
5 andreas 134
using std::bind;
61 andreas 135
using std::pair;
21 andreas 136
using std::string;
51 andreas 137
using std::vector;
5 andreas 138
 
107 andreas 139
string _NO_OBJECT = "The global class TObject is not available!";
140
 
21 andreas 141
/**
142
 * @brief qtmain is used here as the entry point for the surface.
143
 *
144
 * The main entry function parses the command line parameters, if there were
145
 * any and sets the basic attributes. It creates the main window and starts the
146
 * application.
147
 *
148
 * @param argc      The number of command line arguments
149
 * @param argv      A pointer to a 2 dimensional array containing the command
150
 *                  line parameters.
151
 * @param pmanager  A pointer to the page manager class which is the main class
152
 *                  managing everything.
153
 *
154
 * @return If no errors occured it returns 0.
155
 */
3 andreas 156
int qtmain(int argc, char **argv, TPageManager *pmanager)
2 andreas 157
{
31 andreas 158
    DECL_TRACER("qtmain(int argc, char **argv, TPageManager *pmanager)");
2 andreas 159
 
58 andreas 160
    if (!pmanager)
161
    {
162
        MSG_ERROR("Fatal: No pointer to the page manager received!");
163
        return 1;
164
    }
165
 
92 andreas 166
    if (!gPageManager || gPageManager != pmanager)
167
        gPageManager = pmanager;
88 andreas 168
#ifdef __ANDROID__
169
    MSG_INFO("Android API version: " << __ANDROID_API__);
3 andreas 170
 
88 andreas 171
#   if __ANDROID_API__ < 29
172
#       warn "The Android API version is less than 29! Some functions may not work!"
173
#   endif
174
#endif
58 andreas 175
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
38 andreas 176
    QApplication::setAttribute(Qt::AA_ForceRasterWidgets);
178 andreas 177
    QApplication::setAttribute(Qt::AA_Use96Dpi);
178
//    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
179
//    QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
2 andreas 180
#endif
181
 
5 andreas 182
    QApplication app(argc, argv);
58 andreas 183
    // Set the orientation
184
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
185
    QScreen *screen = QGuiApplication::primaryScreen();
130 andreas 186
    QOrientationReading::Orientation ori = QOrientationReading::Undefined;
187
    Qt::ScreenOrientations mask = Qt::PrimaryOrientation;
188
    QOrientationSensor qOri;
189
    QOrientationReading *pORead = qOri.reading();
58 andreas 190
 
130 andreas 191
    if (pORead)
192
    {
193
        ori = pORead->orientation();
194
 
195
        switch(ori)
196
        {
197
            case QOrientationReading::LeftUp:   mask = Qt::InvertedLandscapeOrientation; break;
198
            case QOrientationReading::RightUp:  mask = Qt::LandscapeOrientation; break;
199
            case QOrientationReading::TopDown:  mask = Qt::InvertedPortraitOrientation; break;
200
            default:
201
                mask = Qt::PortraitOrientation;
202
        }
203
    }
204
 
58 andreas 205
    if (!screen)
206
    {
207
        MSG_ERROR("Couldn't determine the primary screen!")
208
        return 1;
209
    }
183 andreas 210
#ifdef QT5_LINUX
151 andreas 211
    if (pmanager->getSettings()->isPortrait())  // portrait?
88 andreas 212
    {
213
        MSG_INFO("Orientation set to portrait mode.");
130 andreas 214
 
215
        if (mask == Qt::InvertedPortraitOrientation)
216
            screen->setOrientationUpdateMask(Qt::InvertedPortraitOrientation);
217
        else
218
            screen->setOrientationUpdateMask(Qt::PortraitOrientation);
88 andreas 219
    }
220
    else
221
    {
222
        MSG_INFO("Orientation set to landscape mode.");
130 andreas 223
 
224
        if (mask == Qt::InvertedLandscapeOrientation)
225
            screen->setOrientationUpdateMask(Qt::InvertedLandscapeOrientation);
226
        else
227
            screen->setOrientationUpdateMask(Qt::LandscapeOrientation);
88 andreas 228
    }
213 andreas 229
#endif  // QT5_LINUX
38 andreas 230
    double scale = 1.0;
212 andreas 231
    double setupScaleFactor = 1.0;
24 andreas 232
    // Calculate the scale factor
233
    if (TConfig::getScale())
234
    {
43 andreas 235
        // Because we've no window here we can not know on which screen, if
236
        // there are more than one, the application will start. Because on a
237
        // mobile mostly no external screen is connected, we take always the
238
        // resolution of the first (built in) screen.
239
        // TODO: Find a way to get the screen the application will start and
240
        // take this screen to calculate the scale factor.
24 andreas 241
        QList<QScreen *> screens = QGuiApplication::screens();
242
        QRect screenGeometry = screens.at(0)->availableGeometry();
88 andreas 243
        double width = 0.0;
244
        double height = 0.0;
212 andreas 245
        double sysWidth = 0.0;
246
        double sysHeight = 0.0;
155 andreas 247
        gScreenWidth = screenGeometry.width();
248
        gScreenHeight = screenGeometry.height();
217 andreas 249
 
250
        if (gScreenWidth < gScreenHeight)
251
        {
252
            int w = gScreenWidth;
253
            gScreenWidth = gScreenHeight;
254
            gScreenHeight = w;
255
        }
256
 
257
        int minWidth = pmanager->getSettings()->getWidth();
24 andreas 258
        int minHeight = pmanager->getSettings()->getHeight();
217 andreas 259
        int minSysWidth = pmanager->getSystemSettings()->getWidth();
212 andreas 260
        int minSysHeight = pmanager->getSystemSettings()->getHeight();
88 andreas 261
 
151 andreas 262
        if (pmanager->getSettings()->isPortrait())  // portrait?
88 andreas 263
        {
217 andreas 264
            width = std::min(gScreenWidth, gScreenHeight);
265
            height = std::max(gScreenHeight, gScreenWidth);
88 andreas 266
        }
267
        else
268
        {
217 andreas 269
            width = std::max(gScreenWidth, gScreenHeight);
270
            height = std::min(gScreenHeight, gScreenWidth);
88 andreas 271
        }
212 andreas 272
        // The setup pages are always landscape
217 andreas 273
        sysWidth = std::max(gScreenWidth, gScreenHeight);
274
        sysHeight = std::min(gScreenHeight, gScreenWidth);
88 andreas 275
 
156 andreas 276
        if (!TConfig::getToolbarSuppress() && TConfig::getToolbarForce())
120 andreas 277
            minWidth += 48;
278
 
31 andreas 279
        MSG_INFO("Dimension of AMX screen:" << minWidth << " x " << minHeight);
88 andreas 280
        MSG_INFO("Screen size: " << width << " x " << height);
43 andreas 281
        // The scale factor is always calculated in difference to the prefered
282
        // size of the original AMX panel.
212 andreas 283
        mScaleFactorW = width / (double)minWidth;
284
        mScaleFactorH = height / (double)minHeight;
217 andreas 285
        double scaleFactorW = sysWidth / (double)minSysWidth;
286
        double scaleFactorH = sysHeight / (double)minSysHeight;
155 andreas 287
        scale = std::min(mScaleFactorW, mScaleFactorH);
212 andreas 288
        setupScaleFactor = std::min(scaleFactorW, scaleFactorH);
235 andreas 289
#ifdef __ANDROID__
212 andreas 290
        __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "scale: %f (Screen: %1.0fx%1.0f, Page: %dx%d)", scale, width, height, minWidth, minHeight);
291
        __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "setupScaleFactor: %f (Screen: %1.0fx%1.0f, Page: %dx%d)", setupScaleFactor, sysWidth, sysHeight, minSysWidth, minSysHeight);
235 andreas 292
#endif
43 andreas 293
        gScale = scale;     // The calculated scale factor
88 andreas 294
        gFullWidth = width;
155 andreas 295
        MSG_INFO("Calculated scale factor: " << scale);
43 andreas 296
        // This preprocessor variable allows the scaling to be done by the Skia
297
        // library, which is used to draw everything. In comparison to Qt this
298
        // library is a bit slower and sometimes does not honor the aspect ratio
44 andreas 299
        // correct. But in case there is another framework than Qt in use, this
43 andreas 300
        // could be necessary.
301
#ifdef _SCALE_SKIA_
26 andreas 302
        if (scale != 0.0)
303
        {
304
            pmanager->setScaleFactor(scale);
31 andreas 305
            MSG_INFO("Scale factor: " << scale);
26 andreas 306
        }
31 andreas 307
 
308
        if (scaleW != 0.0)
309
            pmanager->setScaleFactorWidth(scaleW);
310
 
311
        if (scaleH != 0.0)
312
            pmanager->setScaleFactorHeight(scaleH);
58 andreas 313
#endif  // _SCALE_SKIA_
24 andreas 314
    }
58 andreas 315
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
23 andreas 316
 
235 andreas 317
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
213 andreas 318
    double setupScaleFactor = 1.0;
222 andreas 319
 
320
    if (pmanager->getSettings() != pmanager->getSystemSettings())
211 andreas 321
    {
212 andreas 322
        double width = 0.0;
323
        double height = 0.0;
217 andreas 324
        int minWidth = pmanager->getSystemSettings()->getWidth();
212 andreas 325
        int minHeight = pmanager->getSystemSettings()->getHeight();
326
 
327
        if (!TConfig::getToolbarSuppress() && TConfig::getToolbarForce())
328
            minWidth += 48;
329
 
219 andreas 330
        width = std::max(pmanager->getSettings()->getWidth(), pmanager->getSettings()->getHeight());
331
        height = std::min(pmanager->getSettings()->getHeight(), pmanager->getSettings()->getWidth());
222 andreas 332
 
212 andreas 333
        MSG_INFO("Dimension of AMX screen:" << minWidth << " x " << minHeight);
334
        MSG_INFO("Screen size: " << width << " x " << height);
335
        // The scale factor is always calculated in difference to the prefered
336
        // size of the original AMX panel.
337
        double scaleFactorW = width / (double)minWidth;
338
        double scaleFactorH = height / (double)minHeight;
339
        setupScaleFactor = std::min(scaleFactorW, scaleFactorH);
198 andreas 340
    }
222 andreas 341
#endif
24 andreas 342
    // Initialize the application
164 andreas 343
    pmanager->setDPI(QGuiApplication::primaryScreen()->logicalDotsPerInch());
5 andreas 344
    QCoreApplication::setOrganizationName(TConfig::getProgName().c_str());
164 andreas 345
    QCoreApplication::setApplicationName("TPanel");
24 andreas 346
    QCoreApplication::setApplicationVersion(VERSION_STRING());
5 andreas 347
    QCommandLineParser parser;
348
    parser.setApplicationDescription(QCoreApplication::applicationName());
349
    parser.addHelpOption();
350
    parser.addVersionOption();
351
    parser.process(app);
2 andreas 352
 
5 andreas 353
    MainWindow mainWin;
58 andreas 354
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
355
#   ifndef _SCALE_SKIA_
43 andreas 356
    if (TConfig::getScale() && scale != 1.0)
38 andreas 357
        mainWin.setScaleFactor(scale);
58 andreas 358
#   endif   // _SCALE_SKIA_
359
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
22 andreas 360
    mainWin.setConfigFile(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
43 andreas 361
    mainWin.setStyleSheet("QMainWindow {background: 'black';}");    // Keep the background black. Helps to save battery on OLED displays.
58 andreas 362
    mainWin.grabGesture(Qt::PinchGesture);
153 andreas 363
    mainWin.grabGesture(Qt::SwipeGesture);
38 andreas 364
 
198 andreas 365
    if (setupScaleFactor != 1.0 && setupScaleFactor > 0.0)
366
        mainWin.setSetupScaleFactor(setupScaleFactor);
367
 
5 andreas 368
    mainWin.show();
369
    return app.exec();
2 andreas 370
}
371
 
21 andreas 372
/**
373
 * @brief MainWindow::MainWindow constructor
374
 *
375
 * This method is the constructor for this class. It registers the callback
376
 * functions to the class TPageManager and starts the main run loop.
43 andreas 377
 *
378
 * Qt is used only to manage widgets to handle pages and subpages. A page as
379
 * well as a subpage may contain a background graphic and some elements. The
380
 * elements could be buttons, bargraphs and other objects. The underlying layer
381
 * draw every element as a ready graphic image and call a callback function to
382
 * let Qt display the graphic.
383
 * If there are some animations on a subpage defined, this is also handled by
384
 * Qt. Especialy sliding and fading.
21 andreas 385
 */
2 andreas 386
MainWindow::MainWindow()
387
{
5 andreas 388
    DECL_TRACER("MainWindow::MainWindow()");
59 andreas 389
 
107 andreas 390
    gObject = new TObject;
391
 
59 andreas 392
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
393
    setAttribute(Qt::WA_AcceptTouchEvents, true);   // We accept touch events
394
    grabGesture(Qt::PinchGesture);                  // We use a pinch gesture to open the settings dialog
153 andreas 395
    grabGesture(Qt::SwipeGesture);                  // We support swiping also
88 andreas 396
 
151 andreas 397
    if (gPageManager && gPageManager->getSettings()->isPortrait() == 1)  // portrait?
88 andreas 398
    {
399
        MSG_INFO("Orientation set to portrait mode.");
243 andreas 400
        mOrientation = Qt::PortraitOrientation;
88 andreas 401
        _setOrientation(O_PORTRAIT);
402
    }
403
    else
404
    {
405
        MSG_INFO("Orientation set to landscape mode.");
243 andreas 406
        mOrientation = Qt::LandscapeOrientation;
88 andreas 407
        _setOrientation(O_LANDSCAPE);
408
    }
247 andreas 409
 
410
#ifdef Q_OS_IOS
411
    TIOSRotate::automaticRotation(false);
412
#endif
107 andreas 413
#else
113 andreas 414
    setWindowIcon(QIcon(":images/icon.png"));
59 andreas 415
#endif
92 andreas 416
    if (!gPageManager)
417
    {
418
        EXCEPTFATALMSG("The class TPageManager was not initialized!");
419
    }
420
 
43 andreas 421
    // First we register all our surface callbacks to the underlying work
422
    // layer. All the graphics are drawn by the Skia library. The layer below
423
    // call the following functions to let Qt display the graphics on the
424
    // screen let it manage the widgets containing the graphics.
92 andreas 425
    gPageManager->registerCallbackDB(bind(&MainWindow::_displayButton, this,
5 andreas 426
                                       std::placeholders::_1,
427
                                       std::placeholders::_2,
428
                                       std::placeholders::_3,
429
                                       std::placeholders::_4,
430
                                       std::placeholders::_5,
431
                                       std::placeholders::_6,
432
                                       std::placeholders::_7,
433
                                       std::placeholders::_8));
434
 
92 andreas 435
    gPageManager->registerCallbackSP(bind(&MainWindow::_setPage, this,
5 andreas 436
                                         std::placeholders::_1,
437
                                         std::placeholders::_2,
438
                                         std::placeholders::_3));
439
 
92 andreas 440
    gPageManager->registerCallbackSSP(bind(&MainWindow::_setSubPage, this,
5 andreas 441
                                          std::placeholders::_1,
442
                                          std::placeholders::_2,
443
                                          std::placeholders::_3,
444
                                          std::placeholders::_4,
41 andreas 445
                                          std::placeholders::_5,
217 andreas 446
                                          std::placeholders::_6,
447
                                          std::placeholders::_7));
5 andreas 448
 
92 andreas 449
    gPageManager->registerCallbackSB(bind(&MainWindow::_setBackground, this,
5 andreas 450
                                         std::placeholders::_1,
451
                                         std::placeholders::_2,
452
                                         std::placeholders::_3,
453
                                         std::placeholders::_4,
38 andreas 454
                                         std::placeholders::_5,
455
                                         std::placeholders::_6,
456
                                         std::placeholders::_7));
5 andreas 457
 
92 andreas 458
    gPageManager->regCallDropPage(bind(&MainWindow::_dropPage, this, std::placeholders::_1));
459
    gPageManager->regCallDropSubPage(bind(&MainWindow::_dropSubPage, this, std::placeholders::_1));
460
    gPageManager->regCallPlayVideo(bind(&MainWindow::_playVideo, this,
21 andreas 461
                                       std::placeholders::_1,
462
                                       std::placeholders::_2,
463
                                       std::placeholders::_3,
464
                                       std::placeholders::_4,
465
                                       std::placeholders::_5,
466
                                       std::placeholders::_6,
467
                                       std::placeholders::_7,
468
                                       std::placeholders::_8,
469
                                       std::placeholders::_9));
192 andreas 470
    gPageManager->regCallInputText(bind(&MainWindow::_inputText, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
200 andreas 471
    gPageManager->regCallListBox(bind(&MainWindow::_listBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
190 andreas 472
    gPageManager->registerDropButton(bind(&MainWindow::_dropButton, this, std::placeholders::_1));
92 andreas 473
    gPageManager->regCallbackKeyboard(bind(&MainWindow::_showKeyboard, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
474
    gPageManager->regCallbackKeypad(bind(&MainWindow::_showKeypad, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
475
    gPageManager->regCallResetKeyboard(bind(&MainWindow::_resetKeyboard, this));
476
    gPageManager->regCallShowSetup(bind(&MainWindow::_showSetup, this));
477
    gPageManager->regCallbackResetSurface(bind(&MainWindow::_resetSurface, this));
478
    gPageManager->regCallbackShutdown(bind(&MainWindow::_shutdown, this));
479
    gPageManager->regCallbackPlaySound(bind(&MainWindow::_playSound, this, std::placeholders::_1));
141 andreas 480
    gPageManager->regCallbackStopSound(bind(&MainWindow::_stopSound, this));
481
    gPageManager->regCallbackMuteSound(bind(&MainWindow::_muteSound, this, std::placeholders::_1));
98 andreas 482
    gPageManager->registerCBsetVisible(bind(&MainWindow::_setVisible, this, std::placeholders::_1, std::placeholders::_2));
111 andreas 483
    gPageManager->regSendVirtualKeys(bind(&MainWindow::_sendVirtualKeys, this, std::placeholders::_1));
140 andreas 484
    gPageManager->regShowPhoneDialog(bind(&MainWindow::_showPhoneDialog, this, std::placeholders::_1));
485
    gPageManager->regSetPhoneNumber(bind(&MainWindow::_setPhoneNumber, this, std::placeholders::_1));
486
    gPageManager->regSetPhoneStatus(bind(&MainWindow::_setPhoneStatus, this, std::placeholders::_1));
141 andreas 487
    gPageManager->regSetPhoneState(bind(&MainWindow::_setPhoneState, this, std::placeholders::_1, std::placeholders::_2));
130 andreas 488
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
489
    gPageManager->regOnOrientationChange(bind(&MainWindow::_orientationChanged, this, std::placeholders::_1));
490
#endif
142 andreas 491
    gPageManager->regRepaintWindows(bind(&MainWindow::_repaintWindows, this));
151 andreas 492
    gPageManager->regToFront(bind(&MainWindow::_toFront, this, std::placeholders::_1));
211 andreas 493
#ifndef __ANDROID__
197 andreas 494
    gPageManager->regSetMainWindowSize(bind(&MainWindow::_setSizeMainWindow, this, std::placeholders::_1, std::placeholders::_2));
211 andreas 495
#endif
206 andreas 496
    gPageManager->regDownloadSurface(bind(&MainWindow::_downloadSurface, this, std::placeholders::_1, std::placeholders::_2));
497
    gPageManager->regDisplayMessage(bind(&MainWindow::_displayMessage, this, std::placeholders::_1, std::placeholders::_2));
209 andreas 498
    gPageManager->regFileDialogFunction(bind(&MainWindow::_fileDialog, this,
499
                                             std::placeholders::_1,
500
                                             std::placeholders::_2,
501
                                             std::placeholders::_3,
502
                                             std::placeholders::_4));
142 andreas 503
 
92 andreas 504
    gPageManager->deployCallbacks();
5 andreas 505
    createActions();
506
 
2 andreas 507
#ifndef QT_NO_SESSIONMANAGER
181 andreas 508
#if defined(QT5_LINUX) && !defined(QT6_LINUX)
5 andreas 509
    QGuiApplication::setFallbackSessionManagementEnabled(false);
510
    connect(qApp, &QGuiApplication::commitDataRequest,
511
            this, &MainWindow::writeSettings);
2 andreas 512
#endif
181 andreas 513
#endif
43 andreas 514
    // Some types used to transport data from the layer below.
14 andreas 515
    qRegisterMetaType<size_t>("size_t");
516
    qRegisterMetaType<QByteArray>("QByteArray");
41 andreas 517
    qRegisterMetaType<ANIMATION_t>("ANIMATION_t");
50 andreas 518
    qRegisterMetaType<Button::TButton *>("TButton");
62 andreas 519
    qRegisterMetaType<std::string>("std::string");
14 andreas 520
 
43 andreas 521
    // All the callback functions doesn't act directly. Instead they emit an
522
    // event. Then Qt decides whether the real function is started directly and
523
    // immediately or if the call is queued and called later in a thread. To
524
    // handle this we're "connecting" the real functions to some signals.
15 andreas 525
    try
526
    {
527
        connect(this, &MainWindow::sigDisplayButton, this, &MainWindow::displayButton);
528
        connect(this, &MainWindow::sigSetPage, this, &MainWindow::setPage);
529
        connect(this, &MainWindow::sigSetSubPage, this, &MainWindow::setSubPage);
530
        connect(this, &MainWindow::sigSetBackground, this, &MainWindow::setBackground);
531
        connect(this, &MainWindow::sigDropPage, this, &MainWindow::dropPage);
532
        connect(this, &MainWindow::sigDropSubPage, this, &MainWindow::dropSubPage);
21 andreas 533
        connect(this, &MainWindow::sigPlayVideo, this, &MainWindow::playVideo);
50 andreas 534
        connect(this, &MainWindow::sigInputText, this, &MainWindow::inputText);
200 andreas 535
        connect(this, &MainWindow::sigListBox, this, &MainWindow::listBox);
62 andreas 536
        connect(this, &MainWindow::sigKeyboard, this, &MainWindow::showKeyboard);
537
        connect(this, &MainWindow::sigKeypad, this, &MainWindow::showKeypad);
64 andreas 538
        connect(this, &MainWindow::sigShowSetup, this, &MainWindow::showSetup);
71 andreas 539
        connect(this, &MainWindow::sigPlaySound, this, &MainWindow::playSound);
98 andreas 540
        connect(this, &MainWindow::sigDropButton, this, &MainWindow::dropButton);
541
        connect(this, &MainWindow::sigSetVisible, this, &MainWindow::SetVisible);
111 andreas 542
        connect(this, &MainWindow::sigSendVirtualKeys, this, &MainWindow::sendVirtualKeys);
140 andreas 543
        connect(this, &MainWindow::sigShowPhoneDialog, this, &MainWindow::showPhoneDialog);
544
        connect(this, &MainWindow::sigSetPhoneNumber, this, &MainWindow::setPhoneNumber);
545
        connect(this, &MainWindow::sigSetPhoneStatus, this, &MainWindow::setPhoneStatus);
141 andreas 546
        connect(this, &MainWindow::sigSetPhoneState, this, &MainWindow::setPhoneState);
142 andreas 547
        connect(this, &MainWindow::sigRepaintWindows, this, &MainWindow::repaintWindows);
151 andreas 548
        connect(this, &MainWindow::sigToFront, this, &MainWindow::toFront);
179 andreas 549
        connect(this, &MainWindow::sigOnProgressChanged, this, &MainWindow::onProgressChanged);
247 andreas 550
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
197 andreas 551
        connect(this, &MainWindow::sigSetSizeMainWindow, this, &MainWindow::setSizeMainWindow);
211 andreas 552
#endif
206 andreas 553
        connect(this, &MainWindow::sigDownloadSurface, this, &MainWindow::downloadSurface);
554
        connect(this, &MainWindow::sigDisplayMessage, this, &MainWindow::displayMessage);
209 andreas 555
        connect(this, &MainWindow::sigFileDialog, this, &MainWindow::fileDialog);
213 andreas 556
        connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onAppStateChanged);
130 andreas 557
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
558
        QScreen *screen = QGuiApplication::primaryScreen();
559
        connect(screen, &QScreen::orientationChanged, this, &MainWindow::onScreenOrientationChanged);
560
#endif
15 andreas 561
    }
562
    catch (std::exception& e)
563
    {
564
        MSG_ERROR("Connection error: " << e.what());
565
    }
93 andreas 566
    catch(...)
567
    {
568
        MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
569
    }
15 andreas 570
 
5 andreas 571
    setUnifiedTitleAndToolBarOnMac(true);
182 andreas 572
#ifdef Q_OS_ANDROID
61 andreas 573
    // At least initialize the phone call listener
574
    if (gPageManager)
575
        gPageManager->initPhoneState();
92 andreas 576
 
93 andreas 577
    /*
578
     * In case this class was created not the first time, we initiate a small
579
     * thread to send the signal ApplicationActive to initiate the communication
580
     * with the controller again. This also starts the page manager thread
581
     * (TPageManager), which handles all elements of the surface.
582
     */
92 andreas 583
    if (_restart_ && gPageManager)
584
    {
93 andreas 585
        try
586
        {
587
            std::thread mThread = std::thread([=] { this->_signalState(Qt::ApplicationActive); });
588
            mThread.detach();   // We detach immediately to leave this method.
589
        }
590
        catch (std::exception& e)
591
        {
592
            MSG_ERROR("Error starting the thread to reinvoke communication!");
593
        }
594
        catch(...)
595
        {
596
            MSG_ERROR("Unexpected exception occured [MainWindow::MainWindow()]");
597
        }
92 andreas 598
    }
93 andreas 599
#endif
247 andreas 600
#ifdef Q_OS_IOS
601
    // To get the battery level periodicaly we setup a timer.
602
    if (!mIosBattery)
603
        mIosBattery = new TIOSBattery;
604
 
605
    mIosBattery->update();
606
 
607
    if (gPageManager)
608
    {
609
        int left = mIosBattery->getBatteryLeft();
610
        int state = mIosBattery->getBatteryState();
611
        // At this point no buttons are registered and therefore the battery
612
        // state will not be visible. To have the state at the moment a button
613
        // is registered, we tell the page manager to store the values.
614
        gPageManager->setBattery(left, state);
615
        MSG_DEBUG("Battery state was set to " << left << "% and state " << state);
616
    }
617
#endif  // Q_OS_IOS
618
 
92 andreas 619
    _restart_ = false;
2 andreas 620
}
621
 
34 andreas 622
MainWindow::~MainWindow()
623
{
624
    DECL_TRACER("MainWindow::~MainWindow()");
40 andreas 625
 
34 andreas 626
    killed = true;
627
    prg_stopped = true;
628
 
141 andreas 629
    if (mMediaPlayer)
181 andreas 630
    {
223 andreas 631
#ifdef QT6_LINUX
181 andreas 632
        delete mAudioOutput;
633
#endif
141 andreas 634
        delete mMediaPlayer;
181 andreas 635
    }
141 andreas 636
 
34 andreas 637
    if (gAmxNet && !gAmxNet->isStopped())
638
        gAmxNet->stop();
90 andreas 639
 
640
    if (mToolbar)
641
    {
642
        removeToolBar(mToolbar);
643
        mToolbar = nullptr;
644
    }
645
 
107 andreas 646
    if (gObject)
647
    {
648
        delete gObject;
649
        gObject = nullptr;
650
    }
651
 
90 andreas 652
    isRunning = false;
247 andreas 653
#ifdef Q_OS_IOS
654
    TIOSRotate::automaticRotation(true);
655
 
656
    if (mBatTimer)
657
    {
658
        mBatTimer->stop();
659
        delete mBatTimer;
660
        mBatTimer = nullptr;
661
    }
662
 
663
    if (mIosBattery)
664
    {
665
        delete mIosBattery;
666
        mIosBattery = nullptr;
667
    }
668
#endif
34 andreas 669
}
670
 
235 andreas 671
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
21 andreas 672
/**
93 andreas 673
 * @brief Small thread to invoke the initialization on an Android device.
674
 *
675
 * On Android devices the signal ApplicationActive is not send if the class
676
 * \p MainWindow is destroyed and recreated. Therefore we need this little
677
 * helper to send the signal when the class is initialized.
678
 *
679
 * @param state     This defines the signal to send.
680
 */
681
void MainWindow::_signalState(Qt::ApplicationState state)
682
{
683
    DECL_TRACER("MainWindow::_signalState(Qt::ApplicationState state)");
684
 
685
    std::this_thread::sleep_for(std::chrono::seconds(1));   // Wait a second
213 andreas 686
    onAppStateChanged(state);
93 andreas 687
}
130 andreas 688
 
689
void MainWindow::_orientationChanged(int orientation)
690
{
691
    DECL_TRACER("MainWindow::_orientationChanged(int orientation)");
692
 
151 andreas 693
    if (gPageManager && gPageManager->getSettings()->isPortrait() == 1)  // portrait?
131 andreas 694
    {
695
        if (orientation == O_REVERSE_PORTRAIT && mOrientation != Qt::InvertedPortraitOrientation)
245 andreas 696
        {
131 andreas 697
            _setOrientation((J_ORIENTATION)orientation);
245 andreas 698
            mOrientation = Qt::InvertedPortraitOrientation;
699
        }
131 andreas 700
        else if (orientation == O_PORTRAIT && mOrientation != Qt::PortraitOrientation)
245 andreas 701
        {
131 andreas 702
            _setOrientation((J_ORIENTATION)orientation);
245 andreas 703
            mOrientation = Qt::PortraitOrientation;
704
        }
131 andreas 705
    }
706
    else
707
    {
708
        if (orientation == O_REVERSE_LANDSCAPE && mOrientation != Qt::InvertedLandscapeOrientation)
245 andreas 709
        {
131 andreas 710
            _setOrientation((J_ORIENTATION)orientation);
245 andreas 711
            mOrientation = Qt::InvertedLandscapeOrientation;
712
        }
131 andreas 713
        else if (orientation == O_LANDSCAPE && mOrientation != Qt::LandscapeOrientation)
245 andreas 714
        {
131 andreas 715
            _setOrientation((J_ORIENTATION)orientation);
245 andreas 716
            mOrientation = Qt::LandscapeOrientation;
717
        }
131 andreas 718
    }
130 andreas 719
}
93 andreas 720
 
217 andreas 721
/**
722
 * @brief MainWindow::_freezeWorkaround: A workaround for the screen freeze.
723
 * On Android the screen sometimes stay freezed after the application state
724
 * changes to ACTIVE.
725
 * The workaround produces a faked geometry change which makes the Qt framework
726
 * to reattach to the screen. This workaround is only for QT5.x necessary.
727
 */
728
#ifdef QT5_LINUX
729
void MainWindow::_freezeWorkaround()
730
{
731
    DECL_TRACER("MainWindow::_freezeWorkaround()");
732
 
733
    QScreen* scr = QGuiApplication::screens().first();
734
    QPlatformScreen* l_pScr = scr->handle(); /*QAndroidPlatformScreen*/
735
    QRect l_geomHackAdjustedRect = l_pScr->availableGeometry();
736
    QRect l_geomHackRect = l_geomHackAdjustedRect;
737
    l_geomHackAdjustedRect.adjust(0,0,0,5);
738
    QMetaObject::invokeMethod(dynamic_cast<QObject*>(l_pScr), "setAvailableGeometry", Qt::DirectConnection, Q_ARG( const QRect &, l_geomHackAdjustedRect ));
739
    QMetaObject::invokeMethod(dynamic_cast<QObject*>(l_pScr), "setAvailableGeometry", Qt::QueuedConnection, Q_ARG( const QRect &, l_geomHackRect ));
740
}
741
#endif  // QT5_LINUX
235 andreas 742
#endif  // Q_OS_ANDROID || Q_OS_IOS
217 andreas 743
 
142 andreas 744
void MainWindow::_repaintWindows()
745
{
746
    DECL_TRACER("MainWindow::_repaintWindows()");
747
 
156 andreas 748
    emit sigRepaintWindows();
142 andreas 749
}
151 andreas 750
 
751
void MainWindow::_toFront(ulong handle)
752
{
753
    DECL_TRACER("MainWindow::_toFront(ulong handle)");
754
 
156 andreas 755
    emit sigToFront(handle);
151 andreas 756
}
757
 
206 andreas 758
void MainWindow::_downloadSurface(const string &file, size_t size)
759
{
760
    DECL_TRACER("MainWindow::_downloadSurface(const string &file, size_t size)");
761
 
762
    emit sigDownloadSurface(file, size);
763
}
764
 
93 andreas 765
/**
21 andreas 766
 * @brief MainWindow::closeEvent called when the application receives an exit event.
767
 *
768
 * If the user clicks on the exit icon or on the menu point _Exit_ this method
769
 * is called. It makes sure everything is written to the configuration file
770
 * and accepts the evernt.
771
 *
772
 * @param event The exit event
773
 */
2 andreas 774
void MainWindow::closeEvent(QCloseEvent *event)
775
{
5 andreas 776
    DECL_TRACER("MainWindow::closeEvent(QCloseEvent *event)");
24 andreas 777
#ifdef __ANDROID__
23 andreas 778
    __android_log_print(ANDROID_LOG_DEBUG,"tpanel","Close event; settingsChanged=%s", (settingsChanged ? "true":"false"));
24 andreas 779
#endif
5 andreas 780
    if (settingsChanged)
781
    {
782
        writeSettings();
783
        event->accept();
784
    }
785
    else
786
    {
21 andreas 787
        prg_stopped = true;
34 andreas 788
        killed = true;
36 andreas 789
        MSG_INFO("Program will stop!");
37 andreas 790
#ifdef __ANDROID__
92 andreas 791
        if (gPageManager)
792
            gPageManager->stopNetworkState();
37 andreas 793
#endif
5 andreas 794
        event->accept();
795
    }
2 andreas 796
}
797
 
21 andreas 798
/**
799
 * @brief MainWindow::eventFilter filters the QEvent::MouseMove event
61 andreas 800
 * Beside the filtering of the MouseEvent, it waits for a gesture and
801
 * call the method gestureEvent() which handles the gesture and opens the
802
 * setting dialog.
21 andreas 803
 * @param obj   The object where the event occured.
804
 * @param event The event.
805
 * @return `true` when the event should be ignored.
806
 */
217 andreas 807
bool MainWindow::eventFilter(QObject *, QEvent *event)
15 andreas 808
{
809
    if (event->type() == QEvent::MouseMove)
810
        return true;    // Filter event out, i.e. stop it being handled further.
811
 
59 andreas 812
    if (event->type() == QEvent::Gesture)
813
        return gestureEvent(static_cast<QGestureEvent*>(event));
130 andreas 814
    else if (event->type() == QEvent::OrientationChange)
815
    {
243 andreas 816
        MSG_PROTOCOL("The orientation has changed!");
130 andreas 817
    }
59 andreas 818
 
217 andreas 819
    return false;
15 andreas 820
}
821
 
21 andreas 822
/**
57 andreas 823
 * @brief MainWindow::event Looks for a gesture
824
 * @param event The event occured
825
 * @return TRUE if event was accepted
826
 */
827
bool MainWindow::event(QEvent* event)
828
{
829
    if (event->type() == QEvent::Gesture)
830
        return gestureEvent(static_cast<QGestureEvent*>(event));
130 andreas 831
    else if (event->type() == QEvent::OrientationChange)
832
    {
833
        MSG_TRACE("The orientation has changed!");
834
    }
153 andreas 835
    else if (event->type() == QEvent::KeyPress)
836
    {
837
        QKeyEvent *sKey = static_cast<QKeyEvent*>(event);
57 andreas 838
 
153 andreas 839
        if (sKey && sKey->key() == Qt::Key_Back && !mToolbar)
840
        {
841
            QMessageBox msgBox(this);
842
            msgBox.setText(QString("Should TPanel display the setup dialog or quit?"));
843
            msgBox.addButton("Quit", QMessageBox::AcceptRole);      // This is correct! QT seems to change here the buttons.
844
            msgBox.addButton("Setup", QMessageBox::RejectRole);     // This is correct! QT seems to change here the buttons.
845
            int ret = msgBox.exec();
846
 
847
            if (ret == QMessageBox::Accepted)   // This is correct! QT seems to change here the buttons.
848
            {
849
                showSetup();
850
                return true;
851
            }
852
            else
853
                close();
854
        }
855
    }
856
    else if (event->type() == QEvent::KeyRelease)
857
    {
858
        QKeyEvent *sKey = static_cast<QKeyEvent*>(event);
859
 
860
        if (sKey && sKey->key() == Qt::Key_Back && !mToolbar)
861
            return true;
862
    }
863
 
57 andreas 864
    return QWidget::event(event);
865
}
866
 
867
/**
868
 * @brief MainWindow::gestureEvent handles a pinch event
869
 * If a pinch event occured where the scale factor increased, the settings
870
 * dialog is called. This exists for devices, where the left toolbox is not
871
 * visible.
872
 * @param event The guesture occured
130 andreas 873
 * @return FALSE
57 andreas 874
 */
875
bool MainWindow::gestureEvent(QGestureEvent* event)
876
{
877
    DECL_TRACER("MainWindow::gestureEvent(QGestureEvent* event)");
59 andreas 878
 
57 andreas 879
    if (QGesture *pinch = event->gesture(Qt::PinchGesture))
880
    {
881
        string gs;
882
 
883
        QPinchGesture *pg = static_cast<QPinchGesture *>(pinch);
884
 
885
        switch(pg->state())
886
        {
887
            case Qt::NoGesture:         gs.assign("no gesture"); break;
888
            case Qt::GestureStarted:    gs.assign("gesture started"); break;
889
            case Qt::GestureUpdated:    gs.assign("gesture updated"); break;
890
            case Qt::GestureFinished:   gs.assign("gesture finished"); break;
891
            case Qt::GestureCanceled:   gs.assign("gesture canceled"); break;
892
        }
893
 
59 andreas 894
        MSG_DEBUG("PinchGesture state " << gs << " detected");
57 andreas 895
 
896
        if (pg->state() == Qt::GestureFinished)
897
        {
59 andreas 898
            MSG_DEBUG("total scale: " << pg->totalScaleFactor() << ", scale: " << pg->scaleFactor() << ", last scale: " << pg->lastScaleFactor());
899
 
57 andreas 900
            if (pg->totalScaleFactor() > pg->scaleFactor())
901
                settings();
902
        }
903
    }
153 andreas 904
    else if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
905
    {
906
        string gs;
57 andreas 907
 
153 andreas 908
        if (!gPageManager)
909
            return false;
910
 
911
        QSwipeGesture *sw = static_cast<QSwipeGesture *>(swipe);
912
        MSG_DEBUG("Swipe gesture detected.");
913
 
914
        if (sw->state() == Qt::GestureFinished)
915
        {
916
            if (sw->horizontalDirection() == QSwipeGesture::Left)
917
                gPageManager->onSwipeEvent(TPageManager::SW_LEFT);
918
            else if (sw->horizontalDirection() == QSwipeGesture::Right)
919
                gPageManager->onSwipeEvent(TPageManager::SW_RIGHT);
920
            else if (sw->verticalDirection() == QSwipeGesture::Up)
921
                gPageManager->onSwipeEvent(TPageManager::SW_UP);
922
            else if (sw->verticalDirection() == QSwipeGesture::Down)
923
                gPageManager->onSwipeEvent(TPageManager::SW_DOWN);
924
        }
925
    }
926
 
57 andreas 927
    return false;
928
}
929
 
930
/**
130 andreas 931
 * @brief MainWindow::onScreenOrientationChanged sets invers or normal orientation.
932
 * This method sets according to the actual set orientation the invers
933
 * orientations or switches back to normal orientation.
934
 * For example: When the orientation is set to portrait and the device is turned
935
 * to top down, then the orientation is set to invers portrait.
936
 * @param ori   The detected orientation
937
 */
938
void MainWindow::onScreenOrientationChanged(Qt::ScreenOrientation ori)
939
{
940
    DECL_TRACER("MainWindow::onScreenOrientationChanged(int ori)");
941
 
245 andreas 942
    MSG_PROTOCOL("Orientation changed to " << ori << " (mOrientation: " << mOrientation << ")");
130 andreas 943
 
249 andreas 944
    if (!gPageManager)
130 andreas 945
        return;
946
 
249 andreas 947
    if (gPageManager->getSettings()->isPortrait())
948
    {
949
        if (ori == Qt::PortraitOrientation || ori == Qt::InvertedPortraitOrientation)
950
        {
951
            if (mOrientation == ori)
952
                return;
130 andreas 953
 
249 andreas 954
            mOrientation = ori;
955
        }
956
        else if (mOrientation != Qt::PortraitOrientation && mOrientation != Qt::InvertedPortraitOrientation)
957
            mOrientation = Qt::PortraitOrientation;
958
    }
959
    else
960
    {
961
        if (ori == Qt::LandscapeOrientation || ori == Qt::InvertedLandscapeOrientation)
962
        {
963
            if (mOrientation == ori)
964
                return;
130 andreas 965
 
249 andreas 966
            mOrientation = ori;
967
        }
968
        else if (mOrientation != Qt::LandscapeOrientation && mOrientation != Qt::InvertedLandscapeOrientation)
969
            mOrientation = Qt::LandscapeOrientation;
970
    }
971
 
130 andreas 972
    J_ORIENTATION jori = O_UNDEFINED;
973
 
249 andreas 974
    switch(mOrientation)
130 andreas 975
    {
976
        case Qt::LandscapeOrientation:          jori = O_LANDSCAPE; break;
977
        case Qt::InvertedLandscapeOrientation:  jori = O_REVERSE_LANDSCAPE; break;
978
        case Qt::PortraitOrientation:           jori = O_PORTRAIT; break;
979
        case Qt::InvertedPortraitOrientation:   jori = O_REVERSE_PORTRAIT; break;
980
        default:
981
            return;
982
    }
983
 
984
    _setOrientation(jori);
249 andreas 985
#ifdef Q_OS_IOS
986
    QMargins margins = QASettings::getNotchSize();
987
    MSG_DEBUG("Notch top: " << margins.top() << ", bottom: " << margins.bottom() << ", left: " << margins.left() << ", right: " << margins.right());
988
 
989
    if (gPageManager->getSettings()->isPortrait())
990
    {
991
        gPageManager->setFirstTopPixel(margins.top());
992
        gPageManager->setFirstLeftPixel(margins.left());
993
    }
994
    else
995
    {
996
        gPageManager->setFirstTopPixel(margins.top());
997
 
998
        if (mOrientation == Qt::LandscapeOrientation)
999
            gPageManager->setFirstLeftPixel(margins.left());
1000
        else if (mOrientation == Qt::InvertedLandscapeOrientation)
1001
            gPageManager->setFirstLeftPixel(margins.right());
1002
    }
1003
#endif
1004
 
130 andreas 1005
}
1006
 
1007
/**
140 andreas 1008
 * @brief Displays or hides a phone dialog window.
1009
 * This method creates and displays a phone dialog window containing everything
1010
 * a simple phone needs. Depending on the parameter \p state the dialog is
1011
 * created or an exeisting dialog is closed.
1012
 * @param state     If TRUE the dialog is created or if it is not visible
1013
 * brought to front and is made visible.
1014
 * If this is FALSE an existing dialog window is destroid and disappears. If
1015
 * the window didn't exist nothing happens.
1016
 */
1017
void MainWindow::showPhoneDialog(bool state)
1018
{
1019
    DECL_TRACER("MainWindow::showPhoneDialog(bool state)");
1020
 
1021
    if (mPhoneDialog)
1022
    {
1023
        if (!state)
1024
        {
1025
            mPhoneDialog->close();
1026
            delete mPhoneDialog;
1027
            mPhoneDialog = nullptr;
1028
            return;
1029
        }
1030
 
1031
        if (!mPhoneDialog->isVisible())
1032
            mPhoneDialog->setVisible(true);
1033
 
1034
        return;
1035
    }
1036
 
1037
    if (!state)
1038
        return;
1039
 
1040
    mPhoneDialog = new TQtPhone(this);
243 andreas 1041
#if defined(Q_OS_ANDROID)
140 andreas 1042
    // On mobile devices we set the scale factor always because otherwise the
1043
    // dialog will be unusable.
1044
    mPhoneDialog->setScaleFactor(gScale);
1045
    mPhoneDialog->doResize();
1046
#endif
1047
    mPhoneDialog->open();
1048
}
1049
 
1050
/**
1051
 * Displays a phone number (can also be an URL) on a label in the phone dialog
1052
 * window.
1053
 * @param number    The string contains the phone number to display.
1054
 */
1055
void MainWindow::setPhoneNumber(const std::string& number)
1056
{
1057
    DECL_TRACER("MainWindow::setPhoneNumber(const std::string& number)");
1058
 
1059
    if (!mPhoneDialog)
1060
        return;
1061
 
1062
    mPhoneDialog->setPhoneNumber(number);
1063
}
1064
 
1065
/**
1066
 * Displays a message in the status line on the bottom of the phone dialog
1067
 * window.
1068
 * @param msg   The string conaining a message.
1069
 */
1070
void MainWindow::setPhoneStatus(const std::string& msg)
1071
{
1072
    DECL_TRACER("MainWindow::setPhoneStatus(const std::string& msg)");
1073
 
1074
    if (!mPhoneDialog)
1075
        return;
1076
 
1077
    mPhoneDialog->setPhoneStatus(msg);
1078
}
1079
 
1080
void MainWindow::setPhoneState(int state, int id)
1081
{
1082
    DECL_TRACER("MainWindow::setPhoneState(int state)");
1083
 
1084
    if (mPhoneDialog)
1085
        mPhoneDialog->setPhoneState(state, id);
1086
}
1087
 
1088
/**
39 andreas 1089
 * @brief MainWindow::createActions creates the toolbar on the right side.
21 andreas 1090
 */
2 andreas 1091
void MainWindow::createActions()
1092
{
5 andreas 1093
    DECL_TRACER("MainWindow::createActions()");
2 andreas 1094
 
151 andreas 1095
    // If the toolbar should not be visible at all we return here immediately.
1096
    if (TConfig::getToolbarSuppress())
1097
        return;
1098
 
88 andreas 1099
    // Add a mToolbar (on the right side)
1100
    mToolbar = new QToolBar(this);
58 andreas 1101
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
88 andreas 1102
    mToolbar->setAllowedAreas(Qt::RightToolBarArea);
1103
    mToolbar->setFloatable(false);
1104
    mToolbar->setMovable(false);
243 andreas 1105
#ifdef Q_OS_ANDROID
92 andreas 1106
    if (TConfig::getScale() && gPageManager && gScale != 1.0)
38 andreas 1107
    {
217 andreas 1108
        int width = (int)((double)gPageManager->getSettings()->getWidth() * gScale);
38 andreas 1109
        int tbWidth = (int)(48.0 * gScale);
1110
        int icWidth = (int)(40.0 * gScale);
1111
 
120 andreas 1112
        if ((gFullWidth - width) < tbWidth && !TConfig::getToolbarForce())
38 andreas 1113
        {
88 andreas 1114
            delete mToolbar;
1115
            mToolbar = nullptr;
38 andreas 1116
            return;
1117
        }
1118
 
1119
        QSize iSize(icWidth, icWidth);
88 andreas 1120
        mToolbar->setIconSize(iSize);
38 andreas 1121
    }
243 andreas 1122
#endif  // Q_OS_ANDROID
39 andreas 1123
#else
88 andreas 1124
    mToolbar->setFloatable(true);
1125
    mToolbar->setMovable(true);
1126
    mToolbar->setAllowedAreas(Qt::RightToolBarArea | Qt::BottomToolBarArea);
243 andreas 1127
#endif  // defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
32 andreas 1128
    QAction *arrowUpAct = new QAction(QIcon(":/images/arrow_up.png"), tr("Up"), this);
1129
    connect(arrowUpAct, &QAction::triggered, this, &MainWindow::arrowUp);
88 andreas 1130
    mToolbar->addAction(arrowUpAct);
31 andreas 1131
 
32 andreas 1132
    QAction *arrowLeftAct = new QAction(QIcon(":/images/arrow_left.png"), tr("Left"), this);
1133
    connect(arrowLeftAct, &QAction::triggered, this, &MainWindow::arrowLeft);
88 andreas 1134
    mToolbar->addAction(arrowLeftAct);
31 andreas 1135
 
32 andreas 1136
    QAction *arrowRightAct = new QAction(QIcon(":/images/arrow_right.png"), tr("Right"), this);
1137
    connect(arrowRightAct, &QAction::triggered, this, &MainWindow::arrowRight);
88 andreas 1138
    mToolbar->addAction(arrowRightAct);
31 andreas 1139
 
32 andreas 1140
    QAction *arrowDownAct = new QAction(QIcon(":/images/arrow_down.png"), tr("Down"), this);
1141
    connect(arrowDownAct, &QAction::triggered, this, &MainWindow::arrowDown);
88 andreas 1142
    mToolbar->addAction(arrowDownAct);
31 andreas 1143
 
32 andreas 1144
    QAction *selectOkAct = new QAction(QIcon(":/images/ok.png"), tr("Ok"), this);
1145
    connect(selectOkAct, &QAction::triggered, this, &MainWindow::selectOk);
88 andreas 1146
    mToolbar->addAction(selectOkAct);
31 andreas 1147
 
88 andreas 1148
    mToolbar->addSeparator();
31 andreas 1149
 
35 andreas 1150
    QToolButton *btVolUp = new QToolButton(this);
1151
    btVolUp->setIcon(QIcon(":/images/vol_up.png"));
1152
    connect(btVolUp, &QToolButton::pressed, this, &MainWindow::volumeUpPressed);
1153
    connect(btVolUp, &QToolButton::released, this, &MainWindow::volumeUpReleased);
88 andreas 1154
    mToolbar->addWidget(btVolUp);
40 andreas 1155
 
35 andreas 1156
    QToolButton *btVolDown = new QToolButton(this);
1157
    btVolDown->setIcon(QIcon(":/images/vol_down.png"));
1158
    connect(btVolDown, &QToolButton::pressed, this, &MainWindow::volumeDownPressed);
1159
    connect(btVolDown, &QToolButton::released, this, &MainWindow::volumeDownReleased);
88 andreas 1160
    mToolbar->addWidget(btVolDown);
38 andreas 1161
/*
33 andreas 1162
    QAction *volMute = new QAction(QIcon(":/images/vol_mute.png"), tr("X"), this);
1163
    connect(volMute, &QAction::triggered, this, &MainWindow::volumeMute);
88 andreas 1164
    mToolbar->addAction(volMute);
38 andreas 1165
*/
88 andreas 1166
    mToolbar->addSeparator();
36 andreas 1167
    const QIcon settingsIcon = QIcon::fromTheme("settings-configure", QIcon(":/images/settings.png"));
5 andreas 1168
    QAction *settingsAct = new QAction(settingsIcon, tr("&Settings..."), this);
1169
    settingsAct->setStatusTip(tr("Change the settings"));
1170
    connect(settingsAct, &QAction::triggered, this, &MainWindow::settings);
88 andreas 1171
    mToolbar->addAction(settingsAct);
250 andreas 1172
 
38 andreas 1173
    const QIcon aboutIcon = QIcon::fromTheme("help-about", QIcon(":/images/info.png"));
5 andreas 1174
    QAction *aboutAct = new QAction(aboutIcon, tr("&About..."), this);
1175
    aboutAct->setShortcuts(QKeySequence::Open);
1176
    aboutAct->setStatusTip(tr("About this program"));
1177
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
88 andreas 1178
    mToolbar->addAction(aboutAct);
2 andreas 1179
 
33 andreas 1180
    const QIcon exitIcon = QIcon::fromTheme("application-exit", QIcon(":/images/off.png"));
88 andreas 1181
    QAction *exitAct = mToolbar->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
5 andreas 1182
    exitAct->setShortcuts(QKeySequence::Quit);
1183
    exitAct->setStatusTip(tr("Exit the application"));
31 andreas 1184
 
88 andreas 1185
    addToolBar(Qt::RightToolBarArea, mToolbar);
2 andreas 1186
}
1187
 
21 andreas 1188
/**
1189
 * @brief MainWindow::mousePressEvent catches the event Qt::LeftButton.
1190
 *
1191
 * If the user presses the left mouse button somewhere in the main window, this
1192
 * method is triggered. It retrieves the position of the mouse pointer and
1193
 * sends it to the page manager TPageManager.
1194
 *
1195
 * @param event The event
1196
 */
10 andreas 1197
void MainWindow::mousePressEvent(QMouseEvent* event)
1198
{
70 andreas 1199
    DECL_TRACER("MainWindow::mousePressEvent(QMouseEvent* event)");
1200
 
198 andreas 1201
    if (!gPageManager)
1202
        return;
1203
 
10 andreas 1204
    if(event->button() == Qt::LeftButton)
38 andreas 1205
    {
1206
        int x = event->x();
1207
        int y = event->y();
1208
 
70 andreas 1209
        mLastPressX = x;
1210
        mLastPressY = y;
1211
 
198 andreas 1212
        if (gPageManager->isSetupActive() && isSetupScaled())
38 andreas 1213
        {
198 andreas 1214
            x = (int)((double)x / mSetupScaleFactor);
1215
            y = (int)((double)y / mSetupScaleFactor);
1216
        }
1217
        else if (isScaled())
1218
        {
38 andreas 1219
            x = (int)((double)x / mScaleFactor);
1220
            y = (int)((double)y / mScaleFactor);
1221
        }
1222
 
92 andreas 1223
        gPageManager->mouseEvent(x, y, true);
153 andreas 1224
        mTouchStart = std::chrono::steady_clock::now();
1225
        mTouchX = mLastPressX;
1226
        mTouchY = mLastPressY;
38 andreas 1227
    }
40 andreas 1228
    else if (event->button() == Qt::MiddleButton)
1229
        settings();
10 andreas 1230
}
1231
 
21 andreas 1232
/**
1233
 * @brief MainWindow::mouseReleaseEvent catches the event Qt::LeftButton.
1234
 *
1235
 * If the user releases the left mouse button somewhere in the main window, this
1236
 * method is triggered. It retrieves the position of the mouse pointer and
1237
 * sends it to the page manager TPageManager.
1238
 *
1239
 * @param event The event
1240
 */
10 andreas 1241
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
1242
{
70 andreas 1243
    DECL_TRACER("MainWindow::mouseReleaseEvent(QMouseEvent* event)");
1244
 
153 andreas 1245
    if (!gPageManager)
1246
        return;
1247
 
10 andreas 1248
    if(event->button() == Qt::LeftButton)
38 andreas 1249
    {
101 andreas 1250
        int x = ((mLastPressX >= 0) ? mLastPressX : event->x());
1251
        int y = ((mLastPressY >= 0) ? mLastPressY : event->y());
38 andreas 1252
 
70 andreas 1253
        mLastPressX = mLastPressY = -1;
1254
 
198 andreas 1255
        if (gPageManager->isSetupActive() && isSetupScaled())
38 andreas 1256
        {
198 andreas 1257
            x = (int)((double)x / mSetupScaleFactor);
1258
            y = (int)((double)y / mSetupScaleFactor);
1259
        }
1260
        else if (isScaled())
1261
        {
38 andreas 1262
            x = (int)((double)x / mScaleFactor);
1263
            y = (int)((double)y / mScaleFactor);
1264
        }
1265
 
92 andreas 1266
        gPageManager->mouseEvent(x, y, false);
153 andreas 1267
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
1268
        std::chrono::nanoseconds difftime = end - mTouchStart;
1269
        std::chrono::milliseconds msecs = std::chrono::duration_cast<std::chrono::milliseconds>(difftime);
1270
 
1271
        if (msecs.count() < 100)    // 1/10 of a second
1272
        {
1273
            MSG_DEBUG("Time was too short: " << msecs.count());
1274
            return;
1275
        }
1276
 
1277
        x = event->x();
1278
        y = event->y();
198 andreas 1279
        bool setupActive = gPageManager->isSetupActive();
217 andreas 1280
        int width = (setupActive ? scaleSetup(gPageManager->getSystemSettings()->getWidth()) : scale(gPageManager->getSettings()->getWidth()));
198 andreas 1281
        int height = (setupActive ? scaleSetup(gPageManager->getSystemSettings()->getHeight()) : scale(gPageManager->getSettings()->getHeight()));
153 andreas 1282
        MSG_DEBUG("Coordinates: x1=" << mTouchX << ", y1=" << mTouchY << ", x2=" << x << ", y2=" << y << ", width=" << width << ", height=" << height);
1283
 
1284
        if (mTouchX < x && (x - mTouchX) > (width / 3))
1285
            gPageManager->onSwipeEvent(TPageManager::SW_RIGHT);
1286
        else if (x < mTouchX && (mTouchX - x) > (width / 3))
1287
            gPageManager->onSwipeEvent(TPageManager::SW_LEFT);
1288
        else if (mTouchY < y && (y - mTouchY) > (height / 3))
1289
            gPageManager->onSwipeEvent(TPageManager::SW_DOWN);
1290
        else if (y < mTouchY && (mTouchY - y) > (height / 3))
1291
            gPageManager->onSwipeEvent(TPageManager::SW_UP);
38 andreas 1292
    }
10 andreas 1293
}
1294
 
21 andreas 1295
/**
1296
 * @brief MainWindow::settings initiates the configuration dialog.
1297
 */
2 andreas 1298
void MainWindow::settings()
1299
{
5 andreas 1300
    DECL_TRACER("MainWindow::settings()");
251 andreas 1301
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
238 andreas 1302
#ifndef Q_OS_IOS
197 andreas 1303
    if (gPageManager)
1304
    {
1305
        gPageManager->showSetup();
1306
        return;
1307
    }
211 andreas 1308
    else    // This "else" should never be executed!
1309
        displayMessage("<b>Fatal error</b>: An internal mandatory class was not initialized!<br>Unable to show setup dialog!", "Fatal error");
1310
#else
250 andreas 1311
    QASettings::openSettings();
238 andreas 1312
#endif
1313
#else
90 andreas 1314
    // Save some old values to decide whether to start over or not.
1315
    string oldHost = TConfig::getController();
1316
    int oldPort = TConfig::getPort();
1317
    int oldChannelID = TConfig::getChannel();
118 andreas 1318
    string oldSurface = TConfig::getFtpSurface();
120 andreas 1319
    bool oldToolbar = TConfig::getToolbarForce();
151 andreas 1320
    bool oldToolbarSuppress = TConfig::getToolbarSuppress();
90 andreas 1321
    // Initialize and open the settings dialog.
5 andreas 1322
    TQtSettings *dlg_settings = new TQtSettings(this);
23 andreas 1323
    int ret = dlg_settings->exec();
118 andreas 1324
    bool rebootAnyway = false;
23 andreas 1325
 
179 andreas 1326
    if ((ret && dlg_settings->hasChanged()) || (ret && dlg_settings->downloadForce()))
89 andreas 1327
    {
23 andreas 1328
        writeSettings();
89 andreas 1329
 
151 andreas 1330
        if (!TConfig::getToolbarSuppress() && oldToolbar != TConfig::getToolbarForce())
120 andreas 1331
        {
122 andreas 1332
            QMessageBox msgBox(this);
120 andreas 1333
            msgBox.setText("The change for the visibility of the toolbar will be active on the next start of TPanel!");
1334
            msgBox.exec();
1335
        }
151 andreas 1336
        else if (oldToolbarSuppress != TConfig::getToolbarSuppress() && TConfig::getToolbarSuppress())
1337
        {
1338
            if (mToolbar)
1339
            {
1340
                mToolbar->close();
1341
                delete mToolbar;
1342
                mToolbar = nullptr;
1343
            }
1344
        }
120 andreas 1345
 
122 andreas 1346
        if (TConfig::getFtpSurface() != oldSurface || dlg_settings->downloadForce())
118 andreas 1347
        {
122 andreas 1348
            bool dlYes = true;
118 andreas 1349
 
179 andreas 1350
            MSG_DEBUG("Surface should be downloaded (Old: " << oldSurface << ", New: " << TConfig::getFtpSurface() << ")");
1351
 
122 andreas 1352
            if (!dlg_settings->downloadForce())
1353
            {
1354
                QMessageBox msgBox(this);
1355
                msgBox.setText(QString("Should the surface <b>") + TConfig::getFtpSurface().c_str() + "</b> be installed?");
1356
                msgBox.addButton(QMessageBox::Yes);
1357
                msgBox.addButton(QMessageBox::No);
1358
                int ret = msgBox.exec();
120 andreas 1359
 
122 andreas 1360
                if (ret == QMessageBox::No)
1361
                    dlYes = false;
1362
            }
119 andreas 1363
 
122 andreas 1364
            if (dlYes)
1365
            {
1366
                TTPInit tpinit;
1367
                tpinit.regCallbackProcessEvents(bind(&MainWindow::runEvents, this));
179 andreas 1368
                tpinit.regCallbackProgressBar(bind(&MainWindow::_onProgressChanged, this, std::placeholders::_1));
122 andreas 1369
                tpinit.setPath(TConfig::getProjectPath());
179 andreas 1370
                tpinit.setFileSize(dlg_settings->getSelectedFtpFileSize());
1371
                string msg = "Loading file <b>" + TConfig::getFtpSurface() + "</b>.";
1372
                MSG_DEBUG("Download of surface " << TConfig::getFtpSurface() << " was forced!");
122 andreas 1373
 
179 andreas 1374
                downloadBar(msg, this);
122 andreas 1375
 
1376
                if (tpinit.loadSurfaceFromController(true))
1377
                    rebootAnyway = true;
1378
 
179 andreas 1379
                mDownloadBar->close();
122 andreas 1380
                mBusy = false;
1381
            }
1382
            else
1383
            {
179 andreas 1384
                MSG_DEBUG("No change of surface. Old surface " << oldSurface << " was saved again.");
122 andreas 1385
                TConfig::saveFtpSurface(oldSurface);
1386
                writeSettings();
1387
            }
118 andreas 1388
        }
1389
 
90 andreas 1390
        if (TConfig::getController() != oldHost ||
1391
            TConfig::getChannel() != oldChannelID ||
118 andreas 1392
            TConfig::getPort() != oldPort || rebootAnyway)
89 andreas 1393
        {
90 andreas 1394
            // Start over by exiting this class
1395
            MSG_INFO("Program will start over!");
1396
            _restart_ = true;
1397
            prg_stopped = true;
1398
            killed = true;
1399
 
1400
            if (gAmxNet)
1401
                gAmxNet->stop();
1402
 
1403
            close();
89 andreas 1404
        }
1405
    }
23 andreas 1406
    else if (!ret && dlg_settings->hasChanged())
1407
    {
1408
        TConfig cf(TConfig::getConfigPath() + "/" + TConfig::getConfigFileName());
1409
    }
44 andreas 1410
 
1411
    delete dlg_settings;
251 andreas 1412
#endif  // defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
2 andreas 1413
}
1414
 
21 andreas 1415
/**
1416
 * @brief MainWindow::writeSettings Writes the settings into the configuration file.
1417
 */
2 andreas 1418
void MainWindow::writeSettings()
1419
{
5 andreas 1420
    DECL_TRACER("MainWindow::writeSettings()");
23 andreas 1421
 
1422
    TConfig::saveSettings();
43 andreas 1423
    MSG_INFO("Wrote settings.");
2 andreas 1424
}
1425
 
21 andreas 1426
/**
1427
 * @brief MainWindow::about displays the _about_ dialog.
1428
 */
2 andreas 1429
void MainWindow::about()
1430
{
5 andreas 1431
    DECL_TRACER("MainWindow::about()");
2 andreas 1432
 
82 andreas 1433
    std::string msg = "Simulation of an AMX G4 panel\n";
1434
    msg.append("Version v").append(VERSION_STRING()).append("\n");
88 andreas 1435
    msg.append("(C) Copyright 2020 to 2022 by Andreas Theofilu <andreas@theosys.at>\n");
82 andreas 1436
    msg.append("This program is under the terms of GPL version 3");
1437
 
142 andreas 1438
    QMessageBox::about(this, tr("About TPanel"),
82 andreas 1439
                       tr(msg.c_str()));
2 andreas 1440
}
1441
 
32 andreas 1442
void MainWindow::arrowUp()
1443
{
1444
    DECL_TRACER("MainWindow::arrowUp()");
35 andreas 1445
 
1446
    extButtons_t btType = EXT_CURSOR_UP;
1447
 
1448
    if (TConfig::getPanelType().find("Android") != string::npos)
1449
        btType = EXT_GESTURE_UP;
1450
 
92 andreas 1451
    gPageManager->externalButton(btType, true);
1452
    gPageManager->externalButton(btType, false);
32 andreas 1453
}
14 andreas 1454
 
32 andreas 1455
void MainWindow::arrowLeft()
1456
{
1457
    DECL_TRACER("MainWindow::arrowLeft()");
35 andreas 1458
    extButtons_t btType = EXT_CURSOR_LEFT;
1459
 
1460
    if (TConfig::getPanelType().find("Android") != string::npos)
1461
        btType = EXT_GESTURE_LEFT;
1462
 
92 andreas 1463
    gPageManager->externalButton(btType, true);
1464
    gPageManager->externalButton(btType, false);
32 andreas 1465
}
1466
 
1467
void MainWindow::arrowRight()
1468
{
1469
    DECL_TRACER("MainWindow::arrowRight()");
35 andreas 1470
    extButtons_t btType = EXT_CURSOR_RIGHT;
1471
 
1472
    if (TConfig::getPanelType().find("Android") != string::npos)
1473
        btType = EXT_GESTURE_RIGHT;
1474
 
92 andreas 1475
    gPageManager->externalButton(btType, true);
1476
    gPageManager->externalButton(btType, false);
32 andreas 1477
}
1478
 
1479
void MainWindow::arrowDown()
1480
{
1481
    DECL_TRACER("MainWindow::arrowDown()");
35 andreas 1482
    extButtons_t btType = EXT_CURSOR_DOWN;
1483
 
1484
    if (TConfig::getPanelType().find("Android") != string::npos)
1485
        btType = EXT_GESTURE_DOWN;
1486
 
92 andreas 1487
    gPageManager->externalButton(btType, true);
1488
    gPageManager->externalButton(btType, false);
32 andreas 1489
}
1490
 
1491
void MainWindow::selectOk()
1492
{
1493
    DECL_TRACER("MainWindow::selectOk()");
35 andreas 1494
    extButtons_t btType = EXT_CURSOR_SELECT;
1495
 
1496
    if (TConfig::getPanelType().find("Android") != string::npos)
1497
        btType = EXT_GESTURE_DOUBLE_PRESS;
1498
 
92 andreas 1499
    gPageManager->externalButton(btType, true);
1500
    gPageManager->externalButton(btType, false);
32 andreas 1501
}
1502
 
33 andreas 1503
void MainWindow::volumeUpPressed()
1504
{
1505
    DECL_TRACER("MainWindow::volumeUpPressed()");
35 andreas 1506
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
1507
 
1508
    if (TConfig::getPanelType().find("Android") != string::npos)
1509
        btType = EXT_GESTURE_ROTATE_RIGHT;
1510
 
92 andreas 1511
    gPageManager->externalButton(btType, true);
33 andreas 1512
}
1513
 
1514
void MainWindow::volumeUpReleased()
1515
{
1516
    DECL_TRACER("MainWindow::volumeUpReleased()");
35 andreas 1517
    extButtons_t btType = EXT_CURSOR_ROTATE_RIGHT;
1518
 
1519
    if (TConfig::getPanelType().find("Android") != string::npos)
1520
        btType = EXT_GESTURE_ROTATE_RIGHT;
1521
 
92 andreas 1522
    gPageManager->externalButton(btType, false);
33 andreas 1523
}
1524
 
1525
void MainWindow::volumeDownPressed()
1526
{
1527
    DECL_TRACER("MainWindow::volumeDownPressed()");
35 andreas 1528
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
1529
 
1530
    if (TConfig::getPanelType().find("Android") != string::npos)
1531
        btType = EXT_GESTURE_ROTATE_LEFT;
1532
 
92 andreas 1533
    gPageManager->externalButton(btType, true);
33 andreas 1534
}
1535
 
1536
void MainWindow::volumeDownReleased()
1537
{
1538
    DECL_TRACER("MainWindow::volumeDownReleased()");
35 andreas 1539
    extButtons_t btType = EXT_CURSOR_ROTATE_LEFT;
1540
 
1541
    if (TConfig::getPanelType().find("Android") != string::npos)
1542
        btType = EXT_GESTURE_ROTATE_LEFT;
1543
 
92 andreas 1544
    gPageManager->externalButton(btType, false);
33 andreas 1545
}
38 andreas 1546
/*
33 andreas 1547
void MainWindow::volumeMute()
1548
{
1549
    DECL_TRACER("MainWindow::volumeMute()");
92 andreas 1550
    gPageManager->externalButton(EXT_GENERAL, true);
1551
    gPageManager->externalButton(EXT_GENERAL, false);
33 andreas 1552
}
38 andreas 1553
*/
42 andreas 1554
void MainWindow::animationFinished()
1555
{
1556
    DECL_TRACER("MainWindow::animationFinished()");
43 andreas 1557
 
107 andreas 1558
    if (!gObject)
1559
    {
1560
        MSG_ERROR(_NO_OBJECT);
1561
        return;
1562
    }
43 andreas 1563
 
107 andreas 1564
    TObject::OBJECT_t *obj = gObject->getMarkedRemove();
1565
 
42 andreas 1566
    while (obj)
1567
    {
1568
        if (obj->animation && obj->animation->state() != QAbstractAnimation::Running)
1569
            break;
43 andreas 1570
 
107 andreas 1571
        obj = gObject->getNextMarkedRemove(obj);
42 andreas 1572
    }
43 andreas 1573
 
101 andreas 1574
    if (obj && obj->animation)
42 andreas 1575
    {
107 andreas 1576
        MSG_DEBUG("Dropping object " << TObject::handleToString(obj->handle));
42 andreas 1577
        delete obj->animation;
1578
        obj->animation = nullptr;
107 andreas 1579
        gObject->dropContent(obj);
101 andreas 1580
 
1581
        if (mLastObject == obj)
1582
            mLastObject = nullptr;
1583
 
107 andreas 1584
        gObject->removeObject(obj->handle);
42 andreas 1585
    }
1586
    else
1587
    {
1588
        MSG_WARNING("No or invalid object to delete!");
1589
    }
1590
}
1591
 
142 andreas 1592
void MainWindow::repaintWindows()
1593
{
1594
    DECL_TRACER("MainWindow::repaintWindows()");
1595
 
1596
    if (mWasInactive)
148 andreas 1597
    {
1598
        MSG_INFO("Refreshing of visible popups will be requested.");
142 andreas 1599
        mDoRepaint = true;
148 andreas 1600
    }
142 andreas 1601
}
1602
 
151 andreas 1603
void MainWindow::toFront(ulong handle)
1604
{
1605
    DECL_TRACER("MainWindow::toFront(ulong handle)");
1606
 
1607
    if (!gObject)
1608
    {
1609
        MSG_ERROR(_NO_OBJECT);
1610
        return;
1611
    }
1612
 
1613
    TObject::OBJECT_t *obj = gObject->findObject(handle);
1614
 
1615
    if (!obj)
1616
    {
1617
        MSG_WARNING("Object with " << TObject::handleToString(handle) << " not found!");
1618
        return;
1619
    }
1620
 
1621
    if (obj->type == TObject::OBJ_SUBPAGE && obj->object.widget)
1622
        obj->object.widget->raise();
1623
}
1624
 
206 andreas 1625
void MainWindow::downloadSurface(const string &file, size_t size)
205 andreas 1626
{
206 andreas 1627
    DECL_TRACER("MainWindow::downloadSurface(const string &file, size_t size)");
205 andreas 1628
 
206 andreas 1629
    if (mBusy)
205 andreas 1630
        return;
1631
 
206 andreas 1632
    QMessageBox msgBox(this);
208 andreas 1633
    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 1634
    msgBox.addButton(QMessageBox::Yes);
1635
    msgBox.addButton(QMessageBox::No);
1636
    int ret = msgBox.exec();
1637
 
1638
    if (ret == QMessageBox::Yes)
1639
    {
1640
        TTPInit tpinit;
1641
        tpinit.regCallbackProcessEvents(bind(&MainWindow::runEvents, this));
1642
        tpinit.regCallbackProgressBar(bind(&MainWindow::_onProgressChanged, this, std::placeholders::_1));
1643
        tpinit.setPath(TConfig::getProjectPath());
208 andreas 1644
 
1645
        if (size)
1646
            tpinit.setFileSize(size);
1647
        else
1648
        {
1649
            size = tpinit.getFileSize(file);
1650
 
1651
            if (!size)
1652
            {
1653
                displayMessage("File <b>" + file + "</b> either doesn't exist on " + TConfig::getController() + " or the NetLinx is not reachable!", "Error");
1654
                return;
1655
            }
1656
 
1657
            tpinit.setFileSize(size);
1658
        }
1659
 
206 andreas 1660
        string msg = "Loading file <b>" + file + "</b>.";
1661
 
1662
        downloadBar(msg, this);
1663
        bool reboot = false;
1664
 
1665
        if (tpinit.loadSurfaceFromController(true))
1666
            reboot = true;
1667
        else
1668
            displayMessage("Error downloading file <b>" + file + "</b>!", "Error");
1669
 
1670
        mDownloadBar->close();
208 andreas 1671
        TConfig::setTemporary(true);
1672
        TConfig::saveSettings();
206 andreas 1673
 
1674
        if (reboot)
1675
        {
1676
            // Start over by exiting this class
1677
            MSG_INFO("Program will start over!");
1678
            _restart_ = true;
1679
            prg_stopped = true;
1680
            killed = true;
1681
 
1682
            if (gAmxNet)
1683
                gAmxNet->stop();
1684
 
1685
            close();
1686
        }
1687
    }
1688
 
1689
    mBusy = false;
1690
}
1691
 
1692
void MainWindow::displayMessage(const string &msg, const string &title)
1693
{
1694
    DECL_TRACER("MainWindow::displayMessage(const string &msg, const string &title)");
1695
 
1696
    QMessageBox msgBox(this);
1697
    msgBox.setText(QString(msg.c_str()));
1698
 
1699
    if (!title.empty())
1700
        msgBox.setWindowTitle(title.c_str());
1701
 
1702
    msgBox.addButton(QMessageBox::Ok);
1703
    msgBox.exec();
1704
}
1705
 
209 andreas 1706
void MainWindow::fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)
1707
{
1708
    DECL_TRACER("MainWindow::fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)");
1709
 
1710
    std::string pt = path;
1711
 
1712
    if (fs::exists(path) && fs::is_regular_file(path))
1713
    {
1714
        size_t pos = pt.find_last_of("/");
1715
 
1716
        if (pos != std::string::npos)
1717
            pt = pt.substr(0, pos);
1718
        else
1719
        {
1720
            char hv0[4096];
1721
            getcwd(hv0, sizeof(hv0));
1722
            pt = hv0;
1723
        }
1724
    }
1725
 
1726
    QString actPath(pt.c_str());
1727
    QFileDialog fdialog(this, tr("File"), actPath, tr(extension.c_str()));
1728
    fdialog.setAcceptMode(QFileDialog::AcceptSave);
1729
 
1730
    if (!suffix.empty())
1731
        fdialog.setDefaultSuffix(suffix.c_str());
1732
 
1733
    fdialog.setOption(QFileDialog::DontConfirmOverwrite);
1734
    QString fname;
1735
 
1736
    if (fdialog.exec())
1737
    {
1738
        QDir dir = fdialog.directory();
1739
        QStringList list = fdialog.selectedFiles();
1740
 
1741
        if (list.size() > 0)
1742
            fname = dir.absoluteFilePath(list.at(0));
1743
        else
1744
            return;
1745
    }
1746
    else
1747
        return;
1748
 
1749
#ifdef Q_OS_ANDROID
247 andreas 1750
    // In case of Android we get some kind of URL instead of a clear
209 andreas 1751
    // path. Because of this we must call some Java API functions to find the
1752
    // real path.
1753
    QString fileName = fname;
1754
 
1755
    if (fileName.contains("content://"))
1756
    {
1757
#ifdef QT5_LINUX
1758
        QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod(
1759
            "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;",
1760
            QAndroidJniObject::fromString(fileName).object<jstring>());
1761
 
1762
        fileName = QAndroidJniObject::callStaticObjectMethod(
1763
            "org/qtproject/theosys/UriToPath", "getFileName",
1764
            "(Landroid/net/Uri;Landroid/content/Context;)Ljava/lang/String;",
1765
            uri.object(), QtAndroid::androidContext().object()).toString();
1766
#else   // QT5_LINUX
1767
        // For QT6 the API has slightly changed. Especialy the class name to
1768
        // call Java objects.
1769
        QJniObject uri = QJniObject::callStaticObjectMethod(
1770
            "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;",
1771
            QJniObject::fromString(fileName).object<jstring>());
1772
 
1773
        fileName = QJniObject::callStaticObjectMethod(
1774
            "org/qtproject/theosys/UriToPath", "getFileName",
1775
            "(Landroid/net/Uri;Landroid/content/Context;)Ljava/lang/String;",
1776
            uri.object(), QNativeInterface::QAndroidApplication::context()).toString();
1777
#endif  // QT5_LINUX
1778
        if (fileName.length() > 0)
1779
            fname = fileName;
1780
    }
1781
    else
1782
    {
1783
        MSG_WARNING("Not an Uri? (" << fname.toStdString() << ")");
1784
    }
1785
#endif  // Q_OS_ANDROID
1786
 
1787
    if (gPageManager)
1788
        gPageManager->setTextToButton(handle, fname.toStdString(), true);
1789
}
1790
 
213 andreas 1791
void MainWindow::onTListCallbackCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
206 andreas 1792
{
1793
    DECL_TRACER("MainWindow::on_tlistCallback_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)");
1794
 
1795
    if (!current || current == previous)
1796
        return;
1797
 
205 andreas 1798
    if (!gObject)
1799
    {
1800
        MSG_ERROR(_NO_OBJECT);
1801
        return;
1802
    }
1803
 
1804
    QListWidget *w = current->listWidget();
1805
    TObject::OBJECT_t *objWindow = gObject->findFirstWindow();
1806
 
1807
    while(objWindow)
1808
    {
1809
        TObject::OBJECT_t *objItem = gObject->findFirstChild(objWindow->handle);
1810
 
206 andreas 1811
        while (objItem)
205 andreas 1812
        {
206 andreas 1813
            if (objItem->type == TObject::OBJ_LIST && objItem->object.list == w)
1814
            {
1815
                int row = objItem->object.list->currentRow();
1816
                gPageManager->setSelectedRow(objItem->handle, row + 1, current->text().toStdString());
1817
                return;
1818
            }
1819
 
1820
            objItem = gObject->findNextChild(objItem->handle);
205 andreas 1821
        }
206 andreas 1822
 
1823
        objWindow = gObject->findNextWindow(objWindow);
205 andreas 1824
    }
1825
}
1826
 
179 andreas 1827
void MainWindow::onProgressChanged(int percent)
1828
{
1829
    DECL_TRACER("MainWindow::onProgressChanged(int percent)");
1830
 
1831
    if (!mDownloadBar || !mBusy)
1832
        return;
1833
 
1834
    mDownloadBar->setProgress(percent);
1835
}
247 andreas 1836
#ifdef Q_OS_IOS
1837
void MainWindow::onBatteryTimeout()
1838
{
1839
    if (!mIosBattery || !gPageManager)
1840
        return;
179 andreas 1841
 
247 andreas 1842
    mIosBattery->update();
1843
    gPageManager->informBatteryStatus(mIosBattery->getBatteryLeft(), mIosBattery->getBatteryState());
1844
}
1845
#endif
61 andreas 1846
/**
1847
 * @brief MainWindow::appStateChanged - Is called whenever the state of the app changes.
1848
 * This callback method is called whenever the state of the application
1849
 * changes. This is mostly usefull on mobile devices. Whenever the main window
1850
 * looses the focus (screen closed, application is put into background, ...)
1851
 * this method is called and updates a flag. If the application is not able
1852
 * to draw to the screen (suspended) all events are cached. At the moment the
1853
 * application becomes active, all queued messages are applied.
1854
 * @param state     The new state of the application.
1855
 */
213 andreas 1856
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
31 andreas 1857
{
32 andreas 1858
    DECL_TRACER("MainWindow::appStateChanged(Qt::ApplicationState state)");
31 andreas 1859
 
1860
    switch (state)
1861
    {
61 andreas 1862
        case Qt::ApplicationSuspended:              // Should not occure on a normal desktop
36 andreas 1863
            MSG_INFO("Switched to mode SUSPEND");
31 andreas 1864
            mHasFocus = false;
131 andreas 1865
#ifdef Q_OS_ANDROID
183 andreas 1866
#ifdef QT5_LINUX
131 andreas 1867
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
182 andreas 1868
#else
1869
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
1870
#endif
183 andreas 1871
#endif
250 andreas 1872
#ifdef Q_OS_IOS
1873
            if (mBatTimer)
1874
                mBatTimer->stop();
1875
#endif
31 andreas 1876
        break;
61 andreas 1877
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)      // On a normal desktop we can ignore this signals
31 andreas 1878
        case Qt::ApplicationInactive:
36 andreas 1879
            MSG_INFO("Switched to mode INACTIVE");
31 andreas 1880
            mHasFocus = false;
142 andreas 1881
            mWasInactive = true;
235 andreas 1882
#ifdef Q_OS_ANDROID
183 andreas 1883
#ifdef QT5_LINUX
131 andreas 1884
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
182 andreas 1885
#else
1886
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
1887
#endif
235 andreas 1888
#endif
250 andreas 1889
#ifdef Q_OS_IOS
1890
            if (mBatTimer)
1891
                mBatTimer->stop();
1892
#endif
31 andreas 1893
        break;
1894
 
1895
        case Qt::ApplicationHidden:
36 andreas 1896
            MSG_INFO("Switched to mode HIDDEN");
31 andreas 1897
            mHasFocus = false;
235 andreas 1898
#ifdef Q_OS_ANDROID
183 andreas 1899
#ifdef QT5_LINUX
131 andreas 1900
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
182 andreas 1901
#else
1902
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "pauseOrientationListener", "()V");
1903
#endif
235 andreas 1904
#endif
250 andreas 1905
#ifdef Q_OS_IOS
1906
            if (mBatTimer)
1907
                mBatTimer->stop();
1908
#endif
31 andreas 1909
        break;
61 andreas 1910
#endif
31 andreas 1911
        case Qt::ApplicationActive:
36 andreas 1912
            MSG_INFO("Switched to mode ACTIVE");
31 andreas 1913
            mHasFocus = true;
38 andreas 1914
 
92 andreas 1915
            if (!isRunning && gPageManager)
38 andreas 1916
            {
1917
                // Start the core application
92 andreas 1918
                gPageManager->startUp();
1919
                gPageManager->run();
38 andreas 1920
                isRunning = true;
142 andreas 1921
                mWasInactive = false;
38 andreas 1922
            }
1923
            else
142 andreas 1924
            {
217 andreas 1925
#if defined(QT5_LINUX) && defined(Q_OS_ANDROID)
1926
                _freezeWorkaround();
1927
#endif
38 andreas 1928
                playShowList();
142 andreas 1929
 
1930
                if (mDoRepaint && mWasInactive)
1931
                    repaintObjects();
1932
 
1933
                mDoRepaint = false;
1934
                mWasInactive = false;
1935
            }
131 andreas 1936
#ifdef Q_OS_ANDROID
183 andreas 1937
#ifdef QT5_LINUX
131 andreas 1938
            QAndroidJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "resumeOrientationListener", "()V");
182 andreas 1939
#else
1940
            QJniObject::callStaticMethod<void>("org/qtproject/theosys/Orientation", "resumeOrientationListener", "()V");
131 andreas 1941
#endif
182 andreas 1942
#endif
249 andreas 1943
#ifdef Q_OS_IOS
250 andreas 1944
            if (mBatTimer)
1945
                mBatTimer->start();
1946
 
249 andreas 1947
            {
1948
                QMargins margins = QASettings::getNotchSize();
1949
                MSG_DEBUG("Notch top: " << margins.top() << ", bottom: " << margins.bottom() << ", left: " << margins.left() << ", right: " << margins.right());
1950
 
1951
                if (gPageManager->getSettings()->isPortrait())
1952
                {
1953
                    gPageManager->setFirstTopPixel(margins.top());
1954
                    gPageManager->setFirstLeftPixel(margins.left());
1955
                }
1956
                else
1957
                {
1958
                    gPageManager->setFirstTopPixel(margins.top());
1959
 
1960
                    if (mOrientation == Qt::LandscapeOrientation)
1961
                        gPageManager->setFirstLeftPixel(margins.left());
1962
                    else if (mOrientation == Qt::InvertedLandscapeOrientation)
1963
                        gPageManager->setFirstLeftPixel(margins.right());
1964
                }
1965
            }
1966
#endif
31 andreas 1967
        break;
61 andreas 1968
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
1969
        default:
1970
            mHasFocus = true;
1971
#endif
31 andreas 1972
    }
235 andreas 1973
#if defined(Q_OS_ANDROID)
92 andreas 1974
    if (mHasFocus && gPageManager)
38 andreas 1975
    {
92 andreas 1976
        gPageManager->initNetworkState();
1977
        gPageManager->initBatteryState();
38 andreas 1978
    }
92 andreas 1979
    else if (gPageManager)
38 andreas 1980
    {
92 andreas 1981
        gPageManager->stopNetworkState();
1982
        gPageManager->stopBatteryState();
38 andreas 1983
    }
37 andreas 1984
#endif
31 andreas 1985
}
1986
 
64 andreas 1987
void MainWindow::_shutdown()
1988
{
1989
    DECL_TRACER("MainWindow::_shutdown()")
1990
 
1991
    close();
1992
}
1993
 
32 andreas 1994
/******************* Signal handling *************************/
1995
 
44 andreas 1996
void MainWindow::_resetSurface()
1997
{
1998
    DECL_TRACER("MainWindow::_resetSurface()");
1999
 
90 andreas 2000
    // Start over by exiting this class
2001
    MSG_INFO("Program will start over!");
2002
    _restart_ = true;
2003
    prg_stopped = true;
2004
    killed = true;
88 andreas 2005
 
90 andreas 2006
    if (gAmxNet)
2007
        gAmxNet->stop();
88 andreas 2008
 
90 andreas 2009
    close();
44 andreas 2010
}
2011
 
14 andreas 2012
void MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)
4 andreas 2013
{
14 andreas 2014
    DECL_TRACER("MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)");
2015
 
21 andreas 2016
    if (prg_stopped)
2017
        return;
2018
 
61 andreas 2019
    if (!mHasFocus)     // Suspended?
2020
    {
2021
        addButton(handle, parent, buffer, pixline, left, top, width, height);
2022
        return;
2023
    }
2024
 
14 andreas 2025
    QByteArray buf;
2026
 
2027
    if (buffer && pixline > 0)
2028
    {
2029
        size_t size = width * height * (pixline / width);
15 andreas 2030
        MSG_DEBUG("Buffer size=" << size << ", width=" << width << ", height=" << height << ", left=" << left << ", top=" << top);
14 andreas 2031
        buf.insert(0, (const char *)buffer, size);
2032
    }
2033
 
15 andreas 2034
    try
2035
    {
2036
        emit sigDisplayButton(handle, parent, buf, width, height, pixline, left, top);
2037
    }
2038
    catch (std::exception& e)
2039
    {
2040
        MSG_ERROR("Error triggering function \"displayButton()\": " << e.what());
2041
    }
98 andreas 2042
 
44 andreas 2043
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
14 andreas 2044
}
2045
 
98 andreas 2046
void MainWindow::_setVisible(ulong handle, bool state)
2047
{
2048
    DECL_TRACER("MainWindow::_setVisible(ulong handle, bool state)");
2049
 
2050
    if (prg_stopped)
2051
        return;
2052
 
2053
    emit sigSetVisible(handle, state);
2054
}
2055
 
14 andreas 2056
void MainWindow::_setPage(ulong handle, int width, int height)
2057
{
2058
    DECL_TRACER("MainWindow::_setPage(ulong handle, int width, int height)");
2059
 
21 andreas 2060
    if (prg_stopped)
2061
        return;
2062
 
61 andreas 2063
    if (!mHasFocus)
2064
    {
2065
        addPage(handle, width, height);
2066
        return;
2067
    }
2068
 
14 andreas 2069
    emit sigSetPage(handle, width, height);
182 andreas 2070
#ifndef Q_OS_ANDROID
44 andreas 2071
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2072
#endif
14 andreas 2073
}
2074
 
217 andreas 2075
void MainWindow::_setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate)
14 andreas 2076
{
217 andreas 2077
    DECL_TRACER("MainWindow::_setSubPage(ulong handle, ulong parent, int left, int top, int width, int height)");
14 andreas 2078
 
21 andreas 2079
    if (prg_stopped)
2080
        return;
2081
 
61 andreas 2082
    if (!mHasFocus)
2083
    {
217 andreas 2084
        addSubPage(handle, parent, left, top, width, height, animate);
61 andreas 2085
        return;
2086
    }
2087
 
217 andreas 2088
    emit sigSetSubPage(handle, parent, left, top, width, height, animate);
182 andreas 2089
#ifndef Q_OS_ANDROID
44 andreas 2090
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2091
#endif
14 andreas 2092
}
2093
 
38 andreas 2094
void MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, int width, int height, ulong color)
14 andreas 2095
{
2096
    DECL_TRACER("MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, ulong color)");
2097
 
21 andreas 2098
    if (prg_stopped)
2099
        return;
2100
 
61 andreas 2101
    if (!mHasFocus)
2102
    {
2103
        addBackground(handle, image, size, rowBytes, width, height, color);
2104
        return;
2105
    }
2106
 
14 andreas 2107
    QByteArray buf;
2108
 
2109
    if (image && size > 0)
2110
        buf.insert(0, (const char *)image, size);
2111
 
38 andreas 2112
    emit sigSetBackground(handle, buf, rowBytes, width, height, color);
182 andreas 2113
#ifndef Q_OS_ANDROID
44 andreas 2114
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2115
#endif
14 andreas 2116
}
2117
 
2118
void MainWindow::_dropPage(ulong handle)
2119
{
2120
    DECL_TRACER("MainWindow::_dropPage(ulong handle)");
2121
 
70 andreas 2122
    doReleaseButton();
2123
 
61 andreas 2124
    if (!mHasFocus)
2125
    {
2126
        markDrop(handle);
2127
        return;
2128
    }
2129
 
14 andreas 2130
    emit sigDropPage(handle);
70 andreas 2131
 
182 andreas 2132
#ifndef Q_OS_ANDROID
44 andreas 2133
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2134
#endif
14 andreas 2135
}
2136
 
2137
void MainWindow::_dropSubPage(ulong handle)
2138
{
2139
    DECL_TRACER("MainWindow::_dropSubPage(ulong handle)");
2140
 
70 andreas 2141
    doReleaseButton();
2142
 
61 andreas 2143
    if (!mHasFocus)
2144
    {
2145
        markDrop(handle);
2146
        return;
2147
    }
2148
 
14 andreas 2149
    emit sigDropSubPage(handle);
70 andreas 2150
 
182 andreas 2151
#ifndef Q_OS_ANDROID
44 andreas 2152
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2153
#endif
14 andreas 2154
}
2155
 
98 andreas 2156
void MainWindow::_dropButton(ulong handle)
2157
{
2158
    DECL_TRACER("MainWindow::_dropButton(ulong handle)");
2159
 
2160
    if (!mHasFocus)
2161
    {
2162
        markDrop(handle);
2163
        return;
2164
    }
2165
 
2166
    emit sigDropButton(handle);
2167
}
2168
 
21 andreas 2169
void MainWindow::_playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
2170
{
2171
    DECL_TRACER("MainWindow::_playVideo(ulong handle, const string& url)");
2172
 
2173
    if (prg_stopped)
2174
        return;
2175
 
61 andreas 2176
    if (!mHasFocus)
2177
    {
2178
        addVideo(handle, parent, left, top, width, height, url, user, pw);
2179
        return;
2180
    }
2181
 
21 andreas 2182
    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
182 andreas 2183
#ifndef Q_OS_ANDROID
44 andreas 2184
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2185
#endif
21 andreas 2186
}
2187
 
192 andreas 2188
void MainWindow::_inputText(Button::TButton* button, Button::BITMAP_t& bm, int frame)
50 andreas 2189
{
192 andreas 2190
    DECL_TRACER("MainWindow::_inputText(Button::TButton* button, Button::BITMAP_t& bm, int frame)");
50 andreas 2191
 
208 andreas 2192
    if (prg_stopped || !button)
50 andreas 2193
        return;
2194
 
61 andreas 2195
    if (!mHasFocus)
2196
    {
192 andreas 2197
        addInText(button->getHandle(), button, bm, frame);
61 andreas 2198
        return;
2199
    }
2200
 
52 andreas 2201
    QByteArray buf;
2202
 
2203
    if (bm.buffer && bm.rowBytes > 0)
2204
    {
2205
        size_t size = bm.width * bm.height * (bm.rowBytes / bm.width);
2206
        buf.insert(0, (const char *)bm.buffer, size);
2207
    }
57 andreas 2208
 
192 andreas 2209
    emit sigInputText(button, buf, bm.width, bm.height, frame, bm.rowBytes);
182 andreas 2210
#ifndef Q_OS_ANDROID
50 andreas 2211
    std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_WAIT));
57 andreas 2212
#endif
50 andreas 2213
}
2214
 
200 andreas 2215
void MainWindow::_listBox(Button::TButton* button, Button::BITMAP_t& bm, int frame)
2216
{
2217
    DECL_TRACER("MainWindow::_listBox(Button::TButton* button, Button::BITMAP_t& bm, int frame)");
2218
 
208 andreas 2219
    if (prg_stopped || !button)
200 andreas 2220
        return;
2221
 
2222
    if (!mHasFocus)
2223
    {
2224
        addListBox(button, bm, frame);
2225
        return;
2226
    }
2227
 
2228
    QByteArray buf;
2229
 
2230
    if (bm.buffer && bm.rowBytes > 0)
2231
    {
2232
        size_t size = bm.width * bm.height * (bm.rowBytes / bm.width);
2233
        buf.insert(0, (const char *)bm.buffer, size);
2234
    }
2235
 
2236
    emit sigListBox(button, buf, bm.width, bm.height, frame, bm.rowBytes);
2237
}
2238
 
63 andreas 2239
void MainWindow::_showKeyboard(const std::string& init, const std::string& prompt, bool priv)
62 andreas 2240
{
63 andreas 2241
    DECL_TRACER("MainWindow::_showKeyboard(std::string &init, std::string &prompt, bool priv)");
62 andreas 2242
 
2243
    if (prg_stopped)
2244
        return;
2245
 
70 andreas 2246
    doReleaseButton();
63 andreas 2247
    emit sigKeyboard(init, prompt, priv);
62 andreas 2248
}
2249
 
63 andreas 2250
void MainWindow::_showKeypad(const std::string& init, const std::string& prompt, bool priv)
62 andreas 2251
{
63 andreas 2252
    DECL_TRACER("MainWindow::_showKeypad(std::string &init, std::string &prompt, bool priv)");
62 andreas 2253
 
2254
    if (prg_stopped)
2255
        return;
2256
 
70 andreas 2257
    doReleaseButton();
63 andreas 2258
    emit sigKeypad(init, prompt, priv);
62 andreas 2259
}
2260
 
63 andreas 2261
void MainWindow::_resetKeyboard()
2262
{
2263
    DECL_TRACER("MainWindow::_resetKeyboard()");
2264
 
2265
    emit sigResetKeyboard();
2266
}
2267
 
64 andreas 2268
void MainWindow::_showSetup()
2269
{
2270
    DECL_TRACER("MainWindow::_showSetup()");
2271
 
2272
    emit sigShowSetup();
2273
}
2274
 
71 andreas 2275
void MainWindow::_playSound(const string& file)
2276
{
2277
    DECL_TRACER("MainWindow::_playSound(const string& file)");
2278
 
2279
    emit sigPlaySound(file);
2280
}
2281
 
141 andreas 2282
void MainWindow::_stopSound()
2283
{
2284
    DECL_TRACER("MainWindow::_stopSound()");
2285
 
2286
    emit sigStopSound();
2287
}
2288
 
2289
void MainWindow::_muteSound(bool state)
2290
{
2291
    DECL_TRACER("MainWindow::_muteSound(bool state)");
2292
 
156 andreas 2293
    emit sigMuteSound(state);
141 andreas 2294
}
2295
 
88 andreas 2296
void MainWindow::_setOrientation(J_ORIENTATION ori)
2297
{
182 andreas 2298
#ifdef Q_OS_ANDROID
88 andreas 2299
    DECL_TRACER("MainWindow::_setOriantation(J_ORIENTATION ori)");
2300
 
134 andreas 2301
    if (ori == O_FACE_UP || ori == O_FACE_DOWN)
2302
        return;
183 andreas 2303
#ifdef QT5_LINUX
88 andreas 2304
    QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
182 andreas 2305
#else
183 andreas 2306
    QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative", "activity", "()Landroid/app/Activity;");
182 andreas 2307
#endif
88 andreas 2308
    if ( activity.isValid() )
2309
    {
2310
        activity.callMethod<void>
2311
                ("setRequestedOrientation"  // method name
2312
                 , "(I)V"                   // signature
2313
                 , ori);
131 andreas 2314
 
2315
        switch(ori)
2316
        {
2317
            case O_LANDSCAPE:           mOrientation = Qt::LandscapeOrientation; break;
2318
            case O_PORTRAIT:            mOrientation = Qt::PortraitOrientation; break;
2319
            case O_REVERSE_LANDSCAPE:   mOrientation = Qt::InvertedLandscapeOrientation; break;
2320
            case O_REVERSE_PORTRAIT:    mOrientation = Qt::InvertedPortraitOrientation; break;
2321
            default:
2322
                MSG_WARNING("Orientation is undefined!");
2323
                mOrientation = Qt::PrimaryOrientation;
2324
        }
88 andreas 2325
    }
243 andreas 2326
#elif defined(Q_OS_IOS)
247 andreas 2327
    TIOSRotate::rotate(ori);
237 andreas 2328
#else
2329
    Q_UNUSED(ori);
88 andreas 2330
#endif
2331
}
2332
 
111 andreas 2333
void MainWindow::_sendVirtualKeys(const string& str)
2334
{
2335
    DECL_TRACER("MainWindow::_sendVirtualKeys(const string& str)");
2336
 
2337
    emit sigSendVirtualKeys(str);
2338
}
2339
 
140 andreas 2340
void MainWindow::_showPhoneDialog(bool state)
2341
{
2342
    DECL_TRACER("MainWindow::_showPhoneDialog(bool state)");
2343
 
2344
    emit sigShowPhoneDialog(state);
2345
}
2346
 
2347
void MainWindow::_setPhoneNumber(const std::string& number)
2348
{
2349
    DECL_TRACER("MainWindow::_setPhoneNumber(const std::string& number)");
2350
 
2351
    emit sigSetPhoneNumber(number);
2352
}
2353
 
2354
void MainWindow::_setPhoneStatus(const std::string& msg)
2355
{
2356
    DECL_TRACER("MainWindow::_setPhoneStatus(const std::string& msg)");
2357
 
2358
    emit sigSetPhoneStatus(msg);
2359
}
2360
 
141 andreas 2361
void MainWindow::_setPhoneState(int state, int id)
2362
{
2363
    DECL_TRACER("MainWindow::_setPhoneState(int state, int id)");
2364
 
2365
    emit sigSetPhoneState(state, id);
2366
}
2367
 
179 andreas 2368
void MainWindow::_onProgressChanged(int percent)
2369
{
2370
    DECL_TRACER("MainWindow::_onProgressChanged(int percent)");
2371
 
2372
    emit sigOnProgressChanged(percent);
2373
}
2374
 
206 andreas 2375
void MainWindow::_displayMessage(const string &msg, const string &title)
2376
{
2377
    DECL_TRACER("MainWindow::_displayMessage(const string &msg, const string &title)");
2378
 
2379
    emit sigDisplayMessage(msg, title);
2380
}
2381
 
209 andreas 2382
void MainWindow::_fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)
2383
{
2384
    DECL_TRACER("MainWindow::_fileDialog(ulong handle, const string &path, const std::string& extension, const std::string& suffix)");
2385
 
2386
    if (!handle || path.empty())
2387
    {
2388
        MSG_WARNING("Invalid parameter handle or no path!");
2389
        return;
2390
    }
2391
 
2392
    emit sigFileDialog(handle, path, extension, suffix);
2393
}
211 andreas 2394
#ifndef __ANDROID__
197 andreas 2395
void MainWindow::_setSizeMainWindow(int width, int height)
2396
{
2397
    DECL_TRACER("MainWindow::_setSizeMainWindow(int width, int height)");
2398
 
2399
    emit sigSetSizeMainWindow(width, height);
2400
}
211 andreas 2401
#endif
70 andreas 2402
void MainWindow::doReleaseButton()
2403
{
2404
    DECL_TRACER("MainWindow::doReleaseButton()");
2405
 
92 andreas 2406
    if (mLastPressX >= 0 && mLastPressX >= 0 && gPageManager)
70 andreas 2407
    {
2408
        MSG_DEBUG("Sending outstanding mouse release event for coordinates x" << mLastPressX << ", y" << mLastPressY);
2409
        int x = mLastPressX;
2410
        int y = mLastPressY;
2411
 
2412
        if (isScaled())
2413
        {
2414
            x = (int)((double)x / mScaleFactor);
2415
            y = (int)((double)y / mScaleFactor);
2416
        }
2417
 
92 andreas 2418
        gPageManager->mouseEvent(x, y, false);
70 andreas 2419
    }
2420
 
2421
    mLastPressX = mLastPressY = -1;
2422
}
2423
 
142 andreas 2424
/**
2425
 * @brief MainWindow::repaintObjects
2426
 * On a mobile device it is possible that the content, mostly the background, of
2427
 * a window becomes destroyed and it is not repainted. This may be the case at
2428
 * the moment where the device was disconnected from the controller and
2429
 * reconnected while the application was inactive. In such a case usualy the
2430
 * surface is completely repainted. But because of inactivity this was not
2431
 * possible and some components may look destroyed. They are still functional
2432
 * allthough.
2433
 */
2434
void MainWindow::repaintObjects()
2435
{
2436
    DECL_TRACER("MainWindow::repaintObjects()");
2437
 
2438
    if (!gObject)
2439
    {
2440
        MSG_ERROR(_NO_OBJECT);
2441
        return;
2442
    }
2443
 
2444
    draw_mutex.lock();
154 andreas 2445
#ifdef Q_OS_ANDROID
2446
    std::this_thread::sleep_for(std::chrono::milliseconds(200));    // Necessary for slow devices
2447
#endif
142 andreas 2448
    TObject::OBJECT_t *obj = gObject->findFirstWindow();
2449
 
2450
    while (obj)
2451
    {
2452
        if (!obj->remove && obj->object.widget)
148 andreas 2453
        {
2454
            MSG_PROTOCOL("Refreshing widget " << TObject::handleToString (obj->handle));
154 andreas 2455
            obj->object.widget->repaint();
148 andreas 2456
        }
142 andreas 2457
 
2458
        obj = gObject->findNextWindow(obj);
2459
    }
2460
 
2461
    draw_mutex.unlock();
2462
}
2463
 
141 andreas 2464
int MainWindow::calcVolume(int value)
2465
{
2466
    DECL_TRACER("TQtSettings::calcVolume(int value)");
2467
 
2468
    // volumeSliderValue is in the range [0..100]
181 andreas 2469
#ifdef QT5_LINUX
141 andreas 2470
    qreal linearVolume = QAudio::convertVolume(value / qreal(100.0),
2471
                                               QAudio::LogarithmicVolumeScale,
2472
                                               QAudio::LinearVolumeScale);
2473
 
2474
    return qRound(linearVolume * 100);
181 andreas 2475
#else
2476
    return value;
2477
#endif
141 andreas 2478
}
197 andreas 2479
 
224 andreas 2480
string MainWindow::convertMask(const string& mask)
2481
{
2482
    DECL_TRACER("MainWindow::convertMask(const string& mask)");
2483
 
2484
    string qMask;
2485
 
2486
    for (size_t i = 0; i < mask.length(); ++i)
2487
    {
2488
        switch (mask[i])
2489
        {
2490
            case '0': qMask += "9"; break;
2491
            case '9': qMask += "0"; break;
2492
            case 'A': qMask += "N"; break;
2493
            case 'a': qMask += "n"; break;
2494
            case 'L': qMask += "X"; break;
2495
            case '?': qMask += "x"; break;
2496
            case '&': qMask += "A"; break;
2497
            case 'C': qMask += "a"; break;
2498
            case '^': qMask += ";"; break;
2499
 
2500
            default:
2501
                qMask += mask[i];
2502
        }
2503
    }
2504
 
2505
    return qMask;
2506
}
2507
 
14 andreas 2508
/******************* Draw elements *************************/
2509
 
217 andreas 2510
/**
2511
 * @brief Displays an image.
2512
 * The method is a callback function and is called whenever an image should be
2513
 * displayed. It defines a label, set it to the (scaled) \b width and \b height
2514
 * and moves it to the (scaled) position \b left and \b top.
2515
 *
2516
 * @param handle    The unique handle of the object
2517
 * @param parent    The unique handle of the parent object.
2518
 * @param buffer    A byte array containing the image.
2519
 * @param width     The width of the object
2520
 * @param height    The height of the object
2521
 * @param pixline   The number of pixels in one line of the image.
2522
 * @param left      The prosition from the left.
2523
 * @param top       The position from the top.
2524
 */
14 andreas 2525
void MainWindow::displayButton(ulong handle, ulong parent, QByteArray buffer, int width, int height, int pixline, int left, int top)
2526
{
5 andreas 2527
    DECL_TRACER("MainWindow::displayButton(ulong handle, unsigned char* buffer, size_t size, int width, int height, int pixline, int left, int top)");
4 andreas 2528
 
107 andreas 2529
    if (!gObject)
2530
    {
2531
        MSG_ERROR(_NO_OBJECT);
2532
        return;
2533
    }
2534
 
2535
    draw_mutex.lock();
2536
 
2537
    TObject::OBJECT_t *obj = gObject->findObject(handle);
2538
    TObject::OBJECT_t *par = gObject->findObject(parent);
2539
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << TObject::handleToString(parent));
5 andreas 2540
 
6 andreas 2541
    if (!par)
2542
    {
59 andreas 2543
        if (TStreamError::checkFilter(HLOG_DEBUG))
107 andreas 2544
            MSG_WARNING("Button " << TObject::handleToString(handle) << " has no parent (" << TObject::handleToString(parent) << ")! Ignoring it.");
59 andreas 2545
 
14 andreas 2546
        draw_mutex.unlock();
6 andreas 2547
        return;
2548
    }
2549
 
107 andreas 2550
    if (par->animation && !par->aniDirection)
2551
    {
2552
        if (par->animation->state() == QAbstractAnimation::Running)
2553
        {
2554
            MSG_WARNING("Object " << TObject::handleToString(parent) << " is busy with an animation!");
2555
            par->animation->stop();
2556
        }
2557
        else
2558
        {
2559
            MSG_WARNING("Object " << TObject::handleToString(parent) << " has not finished the animation!");
2560
        }
2561
 
2562
        draw_mutex.unlock();
2563
        return;
2564
    }
2565
    else if (par->remove)
2566
    {
2567
        MSG_WARNING("Object " << TObject::handleToString(parent) << " is marked for remove. Will not draw image!");
2568
        draw_mutex.unlock();
2569
        return;
2570
    }
2571
 
5 andreas 2572
    if (!obj)
2573
    {
107 andreas 2574
        MSG_DEBUG("Adding new object " << TObject::handleToString(handle) << " ...");
2575
        obj = gObject->addObject();
5 andreas 2576
 
2577
        if (!obj)
2578
        {
2579
            MSG_ERROR("Error creating an object!");
2580
            TError::setError();
14 andreas 2581
            draw_mutex.unlock();
5 andreas 2582
            return;
2583
        }
2584
 
107 andreas 2585
        obj->type = TObject::OBJ_BUTTON;
5 andreas 2586
        obj->handle = handle;
40 andreas 2587
 
198 andreas 2588
        if (gPageManager->isSetupActive())
2589
        {
2590
            obj->width = scaleSetup(width);
2591
            obj->height = scaleSetup(height);
2592
            obj->left = scaleSetup(left);
2593
            obj->top = scaleSetup(top);
2594
        }
2595
        else
2596
        {
2597
            obj->width = scale(width);
2598
            obj->height = scale(height);
2599
            obj->left = scale(left);
2600
            obj->top = scale(top);
2601
        }
2602
 
217 andreas 2603
        if (par->type == TObject::OBJ_PAGE)
2604
            obj->object.label = new QLabel("", centralWidget());
2605
        else
40 andreas 2606
            obj->object.label = new QLabel("", par->object.widget);
2607
 
15 andreas 2608
        obj->object.label->installEventFilter(this);
57 andreas 2609
        obj->object.label->grabGesture(Qt::PinchGesture);
153 andreas 2610
        obj->object.label->grabGesture(Qt::SwipeGesture);
59 andreas 2611
        obj->object.label->setFixedSize(obj->width, obj->height);
2612
        obj->object.label->move(obj->left, obj->top);
2613
        obj->object.label->setAttribute(Qt::WA_TransparentForMouseEvents);
6 andreas 2614
    }
14 andreas 2615
    else
107 andreas 2616
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << TObject::objectToString(obj->type) << " found!");
5 andreas 2617
 
187 andreas 2618
    if (obj->type != TObject::OBJ_INPUT)
6 andreas 2619
    {
187 andreas 2620
        try
75 andreas 2621
        {
187 andreas 2622
            if (buffer.size() > 0 && pixline > 0)
107 andreas 2623
            {
187 andreas 2624
                MSG_DEBUG("Setting image for " << TObject::handleToString(handle) << " ...");
2625
                QImage img((unsigned char *)buffer.data(), width, height, pixline, QImage::Format_ARGB32);  // Original size
107 andreas 2626
 
187 andreas 2627
                if (img.isNull() || !img.valid(width-1, height-1))
2628
                {
2629
                    MSG_ERROR("Unable to create a valid image!");
2630
                    draw_mutex.unlock();
2631
                    return;
2632
                }
38 andreas 2633
 
187 andreas 2634
                QSize size(obj->width, obj->height);
2635
                QPixmap pixmap;
2636
                bool ret = false;
38 andreas 2637
 
198 andreas 2638
                if (isScaled() || (gPageManager && gPageManager->isSetupActive()))
187 andreas 2639
                    ret = pixmap.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Scaled size
2640
                else
2641
                    ret = pixmap.convertFromImage(img);
2642
 
2643
                if (!ret || pixmap.isNull())
2644
                {
2645
                    MSG_ERROR("Unable to create a pixmap out of an image!");
2646
                    draw_mutex.unlock();
2647
                    return;
2648
                }
2649
 
2650
                if (obj->object.label)
2651
                    obj->object.label->setPixmap(pixmap);
2652
                else
2653
                {
2654
                    MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist any more!");
2655
                }
107 andreas 2656
            }
2657
 
2658
            if (obj->object.label)
187 andreas 2659
                obj->object.label->show();
75 andreas 2660
        }
187 andreas 2661
        catch(std::exception& e)
2662
        {
2663
            MSG_ERROR("Error drawing button " << TObject::handleToString(handle) << ": " << e.what());
2664
        }
2665
        catch(...)
2666
        {
2667
            MSG_ERROR("Unexpected exception occured [MainWindow::displayButton()]");
2668
        }
5 andreas 2669
    }
75 andreas 2670
 
107 andreas 2671
    gObject->cleanMarked();     // We want to be sure to have no dead entries.
14 andreas 2672
    draw_mutex.unlock();
4 andreas 2673
}
2674
 
98 andreas 2675
void MainWindow::SetVisible(ulong handle, bool state)
2676
{
2677
    DECL_TRACER("MainWindow::SetVisible(ulong handle, bool state)");
2678
 
107 andreas 2679
    if (!gObject)
2680
    {
2681
        MSG_ERROR(_NO_OBJECT);
2682
        return;
2683
    }
98 andreas 2684
 
107 andreas 2685
    TObject::OBJECT_t *obj = gObject->findObject(handle);
2686
 
98 andreas 2687
    if (!obj)
2688
    {
107 andreas 2689
        MSG_ERROR("Object " << TObject::handleToString(handle) << " not found!");
98 andreas 2690
        return;
2691
    }
2692
 
107 andreas 2693
    if (obj->type == TObject::OBJ_BUTTON && obj->object.label)
99 andreas 2694
    {
107 andreas 2695
        MSG_DEBUG("Setting object " << TObject::handleToString(handle) << " visibility to " << (state ? "TRUE" : "FALSE"));
98 andreas 2696
        obj->object.label->setVisible(state);
99 andreas 2697
    }
2698
    else
2699
    {
107 andreas 2700
        MSG_DEBUG("Ignoring non button object " << TObject::handleToString(handle));
99 andreas 2701
    }
98 andreas 2702
}
2703
 
215 andreas 2704
/**
217 andreas 2705
 * @brief Prepares a new object.
215 andreas 2706
 * The method first checks whether there exists a background widget or not. If
2707
 * not it creates a background widget with a black background. On Android this
2708
 * image is the size of the whole screen while on a desktop it is only the size
2709
 * of a page plus the task bar, if any.
217 andreas 2710
 * If the background image (centralWidget()) already exists, it set's only the
2711
 * credentials of the object.
2712
 * It makes sure that all child objects of the central widget are destroyed.
215 andreas 2713
 *
2714
 * @param handle    The handle of the new widget
2715
 * @param width     The original width of the page
2716
 * @param heoght    The original height of the page
2717
 */
5 andreas 2718
void MainWindow::setPage(ulong handle, int width, int height)
2719
{
2720
    DECL_TRACER("MainWindow::setPage(ulong handle, int width, int height)");
4 andreas 2721
 
107 andreas 2722
    if (!gObject)
2723
    {
2724
        MSG_ERROR(_NO_OBJECT);
2725
        return;
2726
    }
2727
 
215 andreas 2728
    if (handle == mActualPageHandle)
2729
        return;
2730
 
107 andreas 2731
    draw_mutex.lock();
217 andreas 2732
    QWidget *wBackground = centralWidget();
107 andreas 2733
 
215 andreas 2734
    // The following should be true only for the first time this method is called.
217 andreas 2735
    if (!wBackground)
215 andreas 2736
    {
2737
        QSize qs = menuBar()->sizeHint();
198 andreas 2738
 
215 andreas 2739
        if (gPageManager && gPageManager->isSetupActive())
2740
            setMinimumSize(scaleSetup(width), scaleSetup(height) + qs.height());
2741
        else
2742
            setMinimumSize(scale(width), scale(height) + qs.height());
2743
    }
198 andreas 2744
 
107 andreas 2745
    TObject::OBJECT_t *obj = gObject->findObject(handle);
5 andreas 2746
 
2747
    if (!obj)
2748
    {
107 andreas 2749
        obj = gObject->addObject();
5 andreas 2750
 
2751
        if (!obj)
2752
        {
107 andreas 2753
            MSG_ERROR("Error crating an object for handle " << TObject::handleToString(handle));
5 andreas 2754
            TError::setError();
14 andreas 2755
            draw_mutex.unlock();
5 andreas 2756
            return;
2757
        }
2758
 
2759
        obj->handle = handle;
107 andreas 2760
        obj->type = TObject::OBJ_PAGE;
217 andreas 2761
        obj->object.widget = nullptr;   // We don't need this because the widget is the centralWidget()
198 andreas 2762
 
217 andreas 2763
        if (gPageManager && gPageManager->isSetupActive())
2764
        {
2765
            obj->height = scaleSetup(height);
2766
            obj->width = scaleSetup(width);
2767
        }
2768
        else
2769
        {
198 andreas 2770
            obj->height = scale(height);
2771
            obj->width = scale(width);
217 andreas 2772
        }
5 andreas 2773
    }
220 andreas 2774
 
217 andreas 2775
    if (!wBackground)
2776
    {
2777
        wBackground = new QWidget();
2778
        wBackground->grabGesture(Qt::PinchGesture);
2779
        wBackground->grabGesture(Qt::SwipeGesture);
2780
        wBackground->setAutoFillBackground(true);
2781
        wBackground->setBackgroundRole(QPalette::Window);
2782
        wBackground->setForegroundRole(QPalette::WindowText);
58 andreas 2783
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
217 andreas 2784
        wBackground->setFixedSize(obj->width, obj->height);
40 andreas 2785
#else
10 andreas 2786
        QRect rectMain = this->geometry();
40 andreas 2787
        QSize icSize =  this->iconSize();
217 andreas 2788
        int lwidth = rectMain.width() + icSize.width() + 16;
2789
        rectMain.setWidth(lwidth);
40 andreas 2790
        setGeometry(rectMain);
146 andreas 2791
        MSG_DEBUG("Height of main window:  " << rectMain.height());
2792
        MSG_DEBUG("Height of panel screen: " << height);
40 andreas 2793
        // If our first top pixel is not 0, maybe because of a menu, window
2794
        // decorations or a toolbar, we must add this extra height to the
2795
        // positions of widgets and mouse presses.
146 andreas 2796
        int avHeight = rectMain.height() - height;
2797
        MSG_DEBUG("Difference in height:   " << avHeight);
92 andreas 2798
        gPageManager->setFirstTopPixel(avHeight);
31 andreas 2799
#endif
217 andreas 2800
    }
227 andreas 2801
 
217 andreas 2802
    if (!centralWidget())
2803
    {
2804
        setCentralWidget(wBackground);
215 andreas 2805
        centralWidget()->show();
6 andreas 2806
    }
227 andreas 2807
 
213 andreas 2808
    mActualPageHandle = handle;
217 andreas 2809
    MSG_PROTOCOL("Current page: " << TObject::handleToString(handle));
14 andreas 2810
    draw_mutex.unlock();
5 andreas 2811
}
2812
 
217 andreas 2813
void MainWindow::setSubPage(ulong handle, ulong parent, int left, int top, int width, int height, ANIMATION_t animate)
5 andreas 2814
{
14 andreas 2815
    draw_mutex.lock();
5 andreas 2816
    DECL_TRACER("MainWindow::setSubPage(ulong handle, int left, int top, int width, int height)");
38 andreas 2817
 
107 andreas 2818
    if (!gObject)
2819
    {
2820
        MSG_ERROR(_NO_OBJECT);
2821
        draw_mutex.unlock();
2822
        return;
2823
    }
2824
 
217 andreas 2825
    TObject::OBJECT_t *par = gObject->findObject(parent);
2826
 
2827
    if (!par || par->type != TObject::OBJ_PAGE)
38 andreas 2828
    {
217 andreas 2829
        MSG_ERROR("Subpage " << TObject::handleToString(handle) << " has no parent or parent is no page! Ignoring it.");
2830
        return;
38 andreas 2831
    }
2832
 
198 andreas 2833
    TObject::OBJECT_t *obj = gObject->findObject(handle);
5 andreas 2834
 
198 andreas 2835
    if (obj)
2836
    {
2837
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " exists. Deleting it and create a new one!");
2838
        gObject->removeAllChilds(handle, false);
2839
        gObject->removeObject(handle, false);
2840
    }
2841
 
2842
    obj = gObject->addObject();
2843
 
5 andreas 2844
    if (!obj)
2845
    {
2846
        MSG_ERROR("Error adding an object!");
2847
        TError::setError();
14 andreas 2848
        draw_mutex.unlock();
5 andreas 2849
        return;
2850
    }
2851
 
198 andreas 2852
    int scLeft;
2853
    int scTop;
2854
    int scWidth;
2855
    int scHeight;
38 andreas 2856
 
198 andreas 2857
    if (gPageManager && gPageManager->isSetupActive())
2858
    {
2859
        scLeft = scaleSetup(left);
2860
        scTop = scaleSetup(top);
2861
        scWidth = scaleSetup(width);
2862
        scHeight = scaleSetup(height);
2863
    }
2864
    else
2865
    {
2866
        scLeft = scale(left);
2867
        scTop = scale(top);
2868
        scWidth = scale(width);
2869
        scHeight = scale(height);
2870
    }
2871
 
107 andreas 2872
    obj->type = TObject::OBJ_SUBPAGE;
5 andreas 2873
    obj->handle = handle;
217 andreas 2874
    obj->object.widget = new QWidget((par->object.widget ? par->object.widget : centralWidget()));
6 andreas 2875
    obj->object.widget->setAutoFillBackground(true);
38 andreas 2876
    obj->object.widget->setFixedSize(scWidth, scHeight);
2877
    obj->object.widget->move(scLeft, scTop);
2878
    obj->left = scLeft;
2879
    obj->top = scTop;
2880
    obj->width = scWidth;
2881
    obj->height = scHeight;
15 andreas 2882
    // filter move event
2883
    obj->object.widget->installEventFilter(this);
57 andreas 2884
    obj->object.widget->grabGesture(Qt::PinchGesture);
153 andreas 2885
    obj->object.widget->grabGesture(Qt::SwipeGesture);
5 andreas 2886
    // By default set a transparent background
38 andreas 2887
    QPixmap pix(scWidth, scHeight);
5 andreas 2888
    pix.fill(QColor::fromRgba(qRgba(0,0,0,0xff)));
2889
    QPalette palette;
18 andreas 2890
    palette.setBrush(QPalette::Window, QBrush(pix));
5 andreas 2891
    obj->object.widget->setPalette(palette);
107 andreas 2892
    obj->aniDirection = true;
31 andreas 2893
 
107 andreas 2894
    startAnimation(obj, animate);
14 andreas 2895
    draw_mutex.unlock();
5 andreas 2896
}
2897
 
38 andreas 2898
void MainWindow::setBackground(ulong handle, QByteArray image, size_t rowBytes, int width, int height, ulong color)
5 andreas 2899
{
14 andreas 2900
    draw_mutex.lock();
28 andreas 2901
    DECL_TRACER("MainWindow::setBackground(ulong handle, QByteArray image, size_t rowBytes, ulong color)");
5 andreas 2902
 
107 andreas 2903
    if (!gObject)
2904
    {
2905
        MSG_ERROR(_NO_OBJECT);
2906
        draw_mutex.unlock();
2907
        return;
2908
    }
5 andreas 2909
 
107 andreas 2910
    TObject::OBJECT_t *obj = gObject->findObject(handle);
2911
 
198 andreas 2912
    if (!obj || obj->remove)
5 andreas 2913
    {
107 andreas 2914
        MSG_WARNING("No object " << TObject::handleToString(handle) << " found!");
14 andreas 2915
        draw_mutex.unlock();
5 andreas 2916
        return;
2917
    }
2918
 
217 andreas 2919
    MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
5 andreas 2920
 
107 andreas 2921
    if (obj->type == TObject::OBJ_BUTTON || obj->type == TObject::OBJ_SUBPAGE)
5 andreas 2922
    {
107 andreas 2923
        MSG_DEBUG("Processing object " << gObject->objectToString(obj->type));
198 andreas 2924
 
2925
        if (obj->type == TObject::OBJ_BUTTON && !obj->object.label)
2926
        {
2927
            MSG_ERROR("The label of the object " << TObject::handleToString(handle) << " was not initialized!");
2928
            draw_mutex.unlock();
2929
            return;
2930
        }
2931
        else if (obj->type == TObject::OBJ_SUBPAGE && !obj->object.widget)
2932
        {
2933
            MSG_ERROR("The widget of the object " << TObject::handleToString(handle) << " was not initialized!");
2934
            draw_mutex.unlock();
2935
            return;
2936
        }
2937
 
6 andreas 2938
        QPixmap pix(obj->width, obj->height);
2939
        pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));
5 andreas 2940
 
14 andreas 2941
        if (image.size() > 0)
5 andreas 2942
        {
38 andreas 2943
            MSG_DEBUG("Setting image of size " << image.size() << " (" << width << " x " << height << ")");
2944
            QImage img((unsigned char *)image.data(), width, height, rowBytes, QImage::Format_ARGB32);
2945
 
198 andreas 2946
            if (isScaled() || (gPageManager && gPageManager->isSetupActive()))
38 andreas 2947
            {
2948
                QSize size(obj->width, obj->height);
2949
                pix.convertFromImage(img.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
2950
            }
2951
            else
2952
                pix.convertFromImage(img);
6 andreas 2953
        }
2954
 
107 andreas 2955
        if (obj->type == TObject::OBJ_BUTTON)
6 andreas 2956
        {
5 andreas 2957
            obj->object.label->setPixmap(pix);
31 andreas 2958
 
2959
            if (mHasFocus)
2960
                obj->object.label->show();
5 andreas 2961
        }
6 andreas 2962
        else
5 andreas 2963
        {
215 andreas 2964
            MSG_DEBUG("Setting image as background for subpage " << ((handle >> 16) & 0x0000ffff));
5 andreas 2965
            QPalette palette;
18 andreas 2966
            palette.setBrush(QPalette::Window, QBrush(pix));
198 andreas 2967
            obj->object.widget->setPalette(palette);
215 andreas 2968
 
2969
            if (mHasFocus)
2970
                obj->object.widget->show();
5 andreas 2971
        }
2972
    }
107 andreas 2973
    else if (obj->type == TObject::OBJ_PAGE)
5 andreas 2974
    {
222 andreas 2975
        QWidget *central = nullptr;
2976
 
2977
        if (!(central = centralWidget()))
5 andreas 2978
        {
215 andreas 2979
            displayMessage("Can't set a background without an active page!", "Internal error");
2980
            return;
5 andreas 2981
        }
219 andreas 2982
#ifdef Q_OS_ANDROID
217 andreas 2983
        if (obj->width <= 0 || obj->height <= 0 || obj->width > gScreenWidth || obj->height > gScreenHeight)
2984
        {
2985
            MSG_ERROR("Invalid page dimensions: " << obj->width << "x" << obj->height << " (max. size: " << gScreenWidth << "x" << gScreenHeight << ")");
2986
            return;
2987
        }
219 andreas 2988
#endif
6 andreas 2989
        QPixmap pix(obj->width, obj->height);
221 andreas 2990
        QColor backgroundColor = QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color)));
2991
        pix.fill(backgroundColor);
223 andreas 2992
        QImage bgImage;
222 andreas 2993
        MSG_PROTOCOL("Filled background of size " << pix.width() << "x" << pix.height() << " with color #" << std::setfill('0') << std::setw(8) << std::hex << color);
6 andreas 2994
 
221 andreas 2995
        if (width > 0 && image.size() >= (width * height * (int)(rowBytes / (unsigned)width)))
5 andreas 2996
        {
38 andreas 2997
            QImage img((unsigned char *)image.data(), width, height, rowBytes, QImage::Format_ARGB32);
221 andreas 2998
            bool valid = false;
38 andreas 2999
 
222 andreas 3000
            if (!img.isNull())
3001
            {
3002
                if (isScaled())
3003
                {
223 andreas 3004
                    bgImage = img.scaled(obj->width, obj->height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
3005
                    valid = pix.convertFromImage(bgImage);
222 andreas 3006
                    MSG_PROTOCOL("Scaled image from " << width << "x" << height << " to " << obj->width << "x" << obj->height);
3007
                }
3008
                else
3009
                {
3010
                    valid = pix.convertFromImage(img);
3011
                    MSG_PROTOCOL("Converted image to pixmap.");
3012
                }
3013
            }
221 andreas 3014
 
3015
            if (!valid || pix.isNull())
3016
            {
222 andreas 3017
                if (pix.isNull())
3018
                    pix = QPixmap(obj->width, obj->height);
3019
 
221 andreas 3020
                pix.fill(backgroundColor);
3021
                MSG_WARNING("Error converting an image! Size raw data: " << image.size() << ", Width: " << width << ", Height: " << height << ", Bytes per row: " << rowBytes);
3022
            }
6 andreas 3023
        }
5 andreas 3024
 
223 andreas 3025
        QPalette palette(central->palette());
222 andreas 3026
        palette.setBrush(QPalette::Window, QBrush(pix));
3027
        central->setPalette(palette);
6 andreas 3028
 
31 andreas 3029
        if (mHasFocus)
223 andreas 3030
        {
222 andreas 3031
            central->show();
223 andreas 3032
#if defined(Q_OS_ANDROID) && defined(QT5_LINUX)
3033
            _freezeWorkaround();
3034
#endif
3035
        }
31 andreas 3036
 
6 andreas 3037
        MSG_DEBUG("Background set");
5 andreas 3038
    }
14 andreas 3039
 
3040
    draw_mutex.unlock();
5 andreas 3041
}
3042
 
11 andreas 3043
void MainWindow::dropPage(ulong handle)
3044
{
14 andreas 3045
    draw_mutex.lock();
11 andreas 3046
    DECL_TRACER("MainWindow::dropPage(ulong handle)");
7 andreas 3047
 
107 andreas 3048
    if (!gObject)
3049
    {
3050
        MSG_ERROR(_NO_OBJECT);
3051
        draw_mutex.unlock();
3052
        return;
3053
    }
11 andreas 3054
 
217 andreas 3055
    MSG_PROTOCOL("Dropping page " << TObject::handleToString(handle));
3056
    gObject->removeAllChilds(handle);
107 andreas 3057
    gObject->removeObject(handle);
14 andreas 3058
    draw_mutex.unlock();
11 andreas 3059
}
3060
 
3061
void MainWindow::dropSubPage(ulong handle)
3062
{
14 andreas 3063
    draw_mutex.lock();
11 andreas 3064
    DECL_TRACER("MainWindow::dropSubPage(ulong handle)");
3065
 
107 andreas 3066
    if (!gObject)
3067
    {
3068
        MSG_ERROR(_NO_OBJECT);
3069
        draw_mutex.unlock();
3070
        return;
3071
    }
11 andreas 3072
 
107 andreas 3073
    TObject::OBJECT_t *obj = gObject->findObject(handle);
3074
 
11 andreas 3075
    if (!obj)
3076
    {
107 andreas 3077
        MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist. Ignoring!");
14 andreas 3078
        draw_mutex.unlock();
11 andreas 3079
        return;
3080
    }
3081
 
217 andreas 3082
    if (obj->type != TObject::OBJ_SUBPAGE)
3083
    {
3084
        draw_mutex.unlock();
3085
        return;
3086
    }
3087
 
3088
    MSG_PROTOCOL("Dropping subpage " << TObject::handleToString(handle));
3089
    gObject->removeAllChilds(handle);
107 andreas 3090
    obj->aniDirection = false;
217 andreas 3091
 
3092
    if (gPageManager && gPageManager->isSetupActive())
3093
        obj->animate.hideEffect = SE_NONE;
3094
 
42 andreas 3095
    startAnimation(obj, obj->animate, false);
107 andreas 3096
    TObject::OBJECT_t *o = mLastObject;
43 andreas 3097
 
42 andreas 3098
    if (obj->animate.hideEffect == SE_NONE || !o)
3099
    {
107 andreas 3100
        gObject->dropContent(obj);
3101
        gObject->removeObject(handle);
42 andreas 3102
    }
3103
 
14 andreas 3104
    draw_mutex.unlock();
11 andreas 3105
}
3106
 
98 andreas 3107
void MainWindow::dropButton(ulong handle)
3108
{
3109
    draw_mutex.lock();
3110
    DECL_TRACER("MainWindow::dropButton(ulong handle)");
3111
 
107 andreas 3112
    if (!gObject)
3113
    {
3114
        MSG_ERROR(_NO_OBJECT);
3115
        draw_mutex.unlock();
3116
        return;
3117
    }
98 andreas 3118
 
107 andreas 3119
    TObject::OBJECT_t *obj = gObject->findObject(handle);
3120
 
98 andreas 3121
    if (!obj)
3122
    {
107 andreas 3123
        MSG_WARNING("Object " << TObject::handleToString(handle) << " does not exist. Ignoring!");
98 andreas 3124
        draw_mutex.unlock();
3125
        return;
3126
    }
3127
 
217 andreas 3128
    if (obj->type == TObject::OBJ_PAGE || obj->type == TObject::OBJ_SUBPAGE)
3129
    {
3130
        draw_mutex.unlock();
3131
        return;
3132
    }
3133
 
107 andreas 3134
    if (obj->type == TObject::OBJ_BUTTON && obj->object.label)
98 andreas 3135
    {
3136
        obj->object.label->close();
3137
        obj->object.label = nullptr;
3138
    }
190 andreas 3139
    else if ((obj->type == TObject::OBJ_INPUT || obj->type == TObject::OBJ_TEXT) && obj->object.plaintext)
3140
    {
3141
        obj->object.plaintext->close();
3142
        obj->object.plaintext = nullptr;
3143
    }
98 andreas 3144
 
107 andreas 3145
    gObject->removeObject(handle);
98 andreas 3146
    draw_mutex.unlock();
3147
}
247 andreas 3148
#if not defined (Q_OS_ANDROID) && not defined(Q_OS_IOS)
197 andreas 3149
void MainWindow::setSizeMainWindow(int width, int height)
3150
{
3151
    DECL_TRACER("MainWindow::setSizeMainWindow(int width, int height)");
3152
 
198 andreas 3153
    QRect geo = geometry();
3154
    setGeometry(geo.x(), geo.y(), width, height + menuBar()->height());
197 andreas 3155
}
211 andreas 3156
#endif
21 andreas 3157
void MainWindow::playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
3158
{
3159
    draw_mutex.lock();
3160
    DECL_TRACER("MainWindow::playVideo(ulong handle, const string& url, const string& user, const string& pw))");
3161
 
107 andreas 3162
    if (!gObject)
3163
    {
3164
        MSG_ERROR(_NO_OBJECT);
3165
        draw_mutex.unlock();
3166
        return;
3167
    }
21 andreas 3168
 
107 andreas 3169
    TObject::OBJECT_t *obj = gObject->findObject(handle);
3170
    TObject::OBJECT_t *par = gObject->findObject(parent);
3171
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << TObject::handleToString(parent));
3172
 
21 andreas 3173
    if (!par)
3174
    {
3175
        MSG_WARNING("Button has no parent! Ignoring it.");
3176
        draw_mutex.unlock();
3177
        return;
3178
    }
3179
 
3180
    if (!obj)
3181
    {
3182
        MSG_DEBUG("Adding new video object ...");
107 andreas 3183
        obj = gObject->addObject();
21 andreas 3184
 
3185
        if (!obj)
3186
        {
3187
            MSG_ERROR("Error creating a video object!");
3188
            TError::setError();
3189
            draw_mutex.unlock();
3190
            return;
3191
        }
3192
 
107 andreas 3193
        obj->type = TObject::OBJ_VIDEO;
21 andreas 3194
        obj->handle = handle;
198 andreas 3195
 
3196
        if (gPageManager && gPageManager->isSetupActive())
3197
        {
3198
            obj->width = scaleSetup(width);
3199
            obj->height = scaleSetup(height);
3200
            obj->left = scaleSetup(left);
3201
            obj->top = scaleSetup(top);
3202
        }
3203
        else
3204
        {
3205
            obj->width = scale(width);
3206
            obj->height = scale(height);
3207
            obj->left = scale(left);
3208
            obj->top = scale(top);
3209
        }
3210
 
21 andreas 3211
        obj->object.vwidget = new QVideoWidget(par->object.widget);
3212
        obj->object.vwidget->installEventFilter(this);
3213
    }
3214
    else
107 andreas 3215
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
21 andreas 3216
 
181 andreas 3217
#if defined(QT5_LINUX) && !defined(QT6_LINUX)
21 andreas 3218
    QMediaPlaylist *playlist = new QMediaPlaylist;
181 andreas 3219
#endif
21 andreas 3220
    QUrl qurl(url.c_str());
3221
 
3222
    if (!user.empty())
3223
        qurl.setUserName(user.c_str());
3224
 
3225
    if (!pw.empty())
3226
        qurl.setPassword(pw.c_str());
3227
 
181 andreas 3228
#if defined(QT5_LINUX) && !defined(QT6_LINUX)
21 andreas 3229
    playlist->addMedia(qurl);
181 andreas 3230
#endif
21 andreas 3231
    obj->player = new QMediaPlayer;
181 andreas 3232
#if defined(QT5_LINUX) && !defined(QT6_LINUX)
21 andreas 3233
    obj->player->setPlaylist(playlist);
181 andreas 3234
#else
3235
    obj->player->setSource(qurl);
3236
#endif
21 andreas 3237
    obj->player->setVideoOutput(obj->object.vwidget);
31 andreas 3238
 
107 andreas 3239
    obj->object.vwidget->show();
3240
    obj->player->play();
21 andreas 3241
}
3242
 
192 andreas 3243
void MainWindow::inputText(Button::TButton* button, QByteArray buf, int width, int height, int frame, size_t pixline)
50 andreas 3244
{
192 andreas 3245
    DECL_TRACER("MainWindow::inputText(Button::TButton* button, QByteArray buf, int width, int height, int frame, size_t pixline)");
50 andreas 3246
 
107 andreas 3247
    if (!gObject)
3248
    {
3249
        MSG_ERROR(_NO_OBJECT);
3250
        return;
3251
    }
3252
 
50 andreas 3253
    if (!button)
3254
    {
3255
        MSG_WARNING("No valid button!");
3256
        return;
3257
    }
3258
 
3259
    ulong handle = button->getHandle();
3260
    ulong parent = button->getParent();
107 andreas 3261
    TObject::OBJECT_t *obj = gObject->findObject(handle);
3262
    TObject::OBJECT_t *par = gObject->findObject(parent);
192 andreas 3263
    MSG_TRACE("Processing button " << TObject::handleToString(handle) << " from parent " << gObject->handleToString(parent) << " with frame width " << frame);
50 andreas 3264
 
3265
    if (!par)
3266
    {
3267
        MSG_WARNING("Button has no parent! Ignoring it.");
3268
        return;
3269
    }
3270
 
3271
    if (!obj)
3272
    {
3273
        MSG_DEBUG("Adding new input object ...");
107 andreas 3274
        obj = gObject->addObject();
50 andreas 3275
 
3276
        if (!obj)
3277
        {
3278
            MSG_ERROR("Error creating an input object!");
3279
            TError::setError();
3280
            return;
3281
        }
3282
 
107 andreas 3283
        obj->type = TObject::OBJ_INPUT;
50 andreas 3284
        obj->handle = handle;
192 andreas 3285
 
198 andreas 3286
        if (gPageManager && gPageManager->isSetupActive())
3287
        {
3288
            obj->width = scaleSetup(width);
3289
            obj->height = scaleSetup(height);
3290
            obj->left = scaleSetup(button->getLeftPosition());
3291
            obj->top = scaleSetup(button->getTopPosition());
3292
        }
3293
        else
3294
        {
3295
            obj->width = scale(width);
3296
            obj->height = scale(height);
3297
            obj->left = scale(button->getLeftPosition());
3298
            obj->top = scale(button->getTopPosition());
3299
        }
3300
 
190 andreas 3301
        string text = button->getText();
224 andreas 3302
        string mask = button->getInputMask();
191 andreas 3303
 
217 andreas 3304
        if (par->type == TObject::OBJ_PAGE)
3305
            obj->object.plaintext = new TQEditLine(text, centralWidget(), button->isMultiLine());
3306
        else
192 andreas 3307
            obj->object.plaintext = new TQEditLine(text, par->object.widget, button->isMultiLine());
191 andreas 3308
 
192 andreas 3309
        obj->object.plaintext->setHandle(handle);
188 andreas 3310
        obj->object.plaintext->setFixedSize(obj->width, obj->height);
3311
        obj->object.plaintext->move(obj->left, obj->top);
192 andreas 3312
        obj->object.plaintext->setPadding(frame, frame, frame, frame);
3313
        obj->object.plaintext->setWordWrapMode(button->getTextWordWrap());
3314
        obj->object.plaintext->setPasswordChar(button->getPasswordChar());
188 andreas 3315
        obj->wid = obj->object.plaintext->winId();
192 andreas 3316
 
213 andreas 3317
        if (gPageManager->isSetupActive())
3318
        {
3319
            int ch = 0;
3320
 
3321
            if (button->getAddressPort() == 0 && button->getAddressChannel() > 0)
3322
                ch = button->getAddressChannel();
3323
            else if (button->getChannelPort() == 0 && button->getChannelNumber() > 0)
3324
                ch = button->getChannelNumber();
3325
 
3326
            switch(ch)
3327
            {
3328
                case SYSTEM_ITEM_SIPPORT:
3329
                case SYSTEM_ITEM_NETLINX_PORT:
3330
                    obj->object.plaintext->setInputMask("000000");
3331
                    obj->object.plaintext->setNumericInput();
3332
                break;
3333
 
3334
                case SYSTEM_ITEM_NETLINX_CHANNEL:
3335
                    obj->object.plaintext->setInputMask("99999");
3336
                    obj->object.plaintext->setNumericInput();
3337
                break;
3338
            }
3339
        }
224 andreas 3340
        else if (!mask.empty())
3341
            obj->object.plaintext->setInputMask(convertMask(mask));
213 andreas 3342
 
52 andreas 3343
        if (!buf.size() || pixline == 0)
51 andreas 3344
        {
3345
            MSG_ERROR("No image!");
3346
            TError::setError();
3347
            return;
3348
        }
3349
 
52 andreas 3350
        MSG_DEBUG("Background image size: " << width << " x " << height << ", rowBytes: " << pixline);
3351
        QPixmap pix(width, height);
3352
        QImage img((uchar *)buf.data(), width, height, QImage::Format_ARGB32);
50 andreas 3353
 
198 andreas 3354
        if (gPageManager && gPageManager->isSetupActive())
3355
            pix.convertFromImage(img.scaled(scaleSetup(width), scaleSetup(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
3356
        else if (isScaled())
52 andreas 3357
            pix.convertFromImage(img.scaled(scale(width), scale(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
50 andreas 3358
        else
3359
            pix.convertFromImage(img);
3360
 
3361
        // Load the font
3362
        FONT_T font = button->getFont();
51 andreas 3363
        vector<string> fontList = TFont::getFontPathList();
3364
        vector<string>::iterator iter;
3365
        string ffile;
50 andreas 3366
 
51 andreas 3367
        for (iter = fontList.begin(); iter != fontList.end(); ++iter)
50 andreas 3368
        {
51 andreas 3369
            TValidateFile vf;
50 andreas 3370
 
51 andreas 3371
            if (!vf.isValidFile(*iter + "/" + font.file))
3372
                continue;
3373
 
3374
            ffile = *iter + "/" + font.file;
3375
            break;
50 andreas 3376
        }
3377
 
51 andreas 3378
        if (ffile.empty())
3379
        {
3380
            MSG_ERROR("Font " << font.file << " doesn't exists!");
3381
            return;
3382
        }
3383
 
213 andreas 3384
        if (QFontDatabase::addApplicationFont(ffile.c_str()) == -1)
50 andreas 3385
        {
3386
            MSG_ERROR("Font " << ffile << " could not be loaded!");
3387
            TError::setError();
3388
            return;
3389
        }
3390
 
3391
        QFont ft;
3392
        ft.setFamily(font.name.c_str());
213 andreas 3393
        int eighty = (int)((double)button->getHeight() / 100.0 * 85.0);
198 andreas 3394
 
3395
        if (gPageManager && gPageManager->isSetupActive())
213 andreas 3396
            ft.setPixelSize(scaleSetup(eighty - frame * 2));
198 andreas 3397
        else
213 andreas 3398
            ft.setPixelSize(scale(eighty - frame * 2));
198 andreas 3399
 
213 andreas 3400
        MSG_DEBUG("Using font \"" << font.name << "\" with size " << font.size << "px.");
50 andreas 3401
 
3402
        switch (button->getFontStyle())
3403
        {
3404
            case FONT_BOLD:     ft.setBold(true); break;
3405
            case FONT_ITALIC:   ft.setItalic(true); break;
3406
            case FONT_BOLD_ITALIC:
3407
                ft.setBold(true);
3408
                ft.setItalic(true);
3409
                break;
3410
 
3411
            default:
3412
                ft.setBold(false);
3413
                ft.setItalic(false);
3414
        }
3415
 
52 andreas 3416
        QPalette palette;
50 andreas 3417
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor());
52 andreas 3418
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor());
51 andreas 3419
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
52 andreas 3420
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
3421
        palette.setColor(QPalette::Base, cfcolor);
51 andreas 3422
        palette.setColor(QPalette::Text, txcolor);
52 andreas 3423
        //        pix.save("frame.png");
188 andreas 3424
        palette.setBrush(QPalette::Base, QBrush(pix));
3425
 
3426
        obj->object.plaintext->setFont(ft);
3427
        obj->object.plaintext->setPalette(palette);
3428
        obj->object.plaintext->show();
50 andreas 3429
    }
3430
    else
206 andreas 3431
    {
107 andreas 3432
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
206 andreas 3433
 
3434
        string text = button->getText();
224 andreas 3435
        string mask = button->getInputMask();
206 andreas 3436
        obj->object.plaintext->setText(text);
224 andreas 3437
 
3438
        if (!mask.empty())
3439
            obj->object.plaintext->setInputMask(convertMask(mask));
3440
 
206 andreas 3441
        gObject->cleanMarked();     // We want to be sure to have no dead entries.
3442
        obj->object.plaintext->show();
3443
    }
50 andreas 3444
}
62 andreas 3445
 
200 andreas 3446
void MainWindow::listBox(Button::TButton* button, QByteArray buffer, int width, int height, int frame, size_t pixline)
3447
{
3448
    DECL_TRACER("MainWindow::listBox(Button::TButton* button, QByteArray buffer, int width, int height, int frame, size_t pixline)");
3449
 
3450
    if (!gObject)
3451
    {
3452
        MSG_ERROR(_NO_OBJECT);
3453
        return;
3454
    }
3455
 
3456
    if (!button)
3457
    {
3458
        MSG_WARNING("No valid button!");
3459
        return;
3460
    }
3461
 
3462
    ulong handle = button->getHandle();
3463
    ulong parent = button->getParent();
3464
    TObject::OBJECT_t *obj = gObject->findObject(handle);
3465
    TObject::OBJECT_t *par = gObject->findObject(parent);
3466
    MSG_TRACE("Processing list " << TObject::handleToString(handle) << " from parent " << gObject->handleToString(parent) << " with frame width " << frame);
3467
 
3468
    if (!par)
3469
    {
3470
        MSG_WARNING("List has no parent! Ignoring it.");
3471
        return;
3472
    }
3473
 
3474
    if (!obj)
3475
    {
3476
        MSG_DEBUG("Adding new list object ...");
3477
        obj = gObject->addObject();
3478
 
3479
        if (!obj)
3480
        {
3481
            MSG_ERROR("Error creating a list object!");
3482
            TError::setError();
3483
            return;
3484
        }
3485
 
3486
        obj->type = TObject::OBJ_LIST;
3487
        obj->handle = handle;
3488
        obj->rows = button->getListNumRows();
3489
        obj->cols = button->getListNumCols();
3490
 
3491
        if (gPageManager && gPageManager->isSetupActive())
3492
        {
3493
            obj->width = scaleSetup(width);
3494
            obj->height = scaleSetup(height);
3495
            obj->left = scaleSetup(button->getLeftPosition());
3496
            obj->top = scaleSetup(button->getTopPosition());
3497
        }
3498
        else
3499
        {
3500
            obj->width = scale(width);
3501
            obj->height = scale(height);
3502
            obj->left = scale(button->getLeftPosition());
3503
            obj->top = scale(button->getTopPosition());
3504
        }
3505
 
3506
        vector<string> listContent = button->getListContent();
3507
 
217 andreas 3508
        if (par->type == TObject::OBJ_PAGE)
3509
            obj->object.list = new QListWidget(centralWidget());
3510
        else
200 andreas 3511
            obj->object.list = new QListWidget(par->object.widget);
3512
 
3513
        obj->object.list->setFixedSize(obj->width, obj->height);
3514
        obj->object.list->move(obj->left, obj->top);
213 andreas 3515
        connect(obj->object.list, &QListWidget::currentItemChanged, this, &MainWindow::onTListCallbackCurrentItemChanged);
200 andreas 3516
 
3517
        if (!buffer.size() || pixline == 0)
3518
        {
3519
            MSG_ERROR("No image!");
3520
            TError::setError();
3521
            return;
3522
        }
3523
 
3524
        MSG_DEBUG("Background image size: " << width << " x " << height << ", rowBytes: " << pixline);
3525
        QPixmap pix(width, height);
3526
        QImage img((uchar *)buffer.data(), width, height, QImage::Format_ARGB32);
3527
 
3528
        if (gPageManager && gPageManager->isSetupActive())
3529
            pix.convertFromImage(img.scaled(scaleSetup(width), scaleSetup(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
3530
        else if (isScaled())
3531
            pix.convertFromImage(img.scaled(scale(width), scale(height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
3532
        else
3533
            pix.convertFromImage(img);
3534
 
3535
        // Load the font
3536
        FONT_T font = button->getFont();
3537
        vector<string> fontList = TFont::getFontPathList();
3538
        vector<string>::iterator iter;
3539
        string ffile;
3540
 
3541
        for (iter = fontList.begin(); iter != fontList.end(); ++iter)
3542
        {
3543
            TValidateFile vf;
3544
 
3545
            if (!vf.isValidFile(*iter + "/" + font.file))
3546
                continue;
3547
 
3548
            ffile = *iter + "/" + font.file;
3549
            break;
3550
        }
3551
 
3552
        if (ffile.empty())
3553
        {
3554
            MSG_ERROR("Font " << font.file << " doesn't exists!");
3555
            return;
3556
        }
3557
 
213 andreas 3558
        if (QFontDatabase::addApplicationFont(ffile.c_str()) == -1)
200 andreas 3559
        {
3560
            MSG_ERROR("Font " << ffile << " could not be loaded!");
3561
            TError::setError();
3562
            return;
3563
        }
3564
 
3565
        QFont ft;
3566
        ft.setFamily(font.name.c_str());
3567
 
213 andreas 3568
        if (gPageManager && gPageManager->isSetupActive())
3569
            ft.setPointSize(scaleSetup(font.size));
3570
        else
200 andreas 3571
            ft.setPointSize(font.size);
3572
 
3573
        MSG_DEBUG("Using font \"" << font.name << "\" with size " << font.size << "pt.");
3574
 
3575
        switch (button->getFontStyle())
3576
        {
3577
            case FONT_BOLD:     ft.setBold(true); break;
3578
            case FONT_ITALIC:   ft.setItalic(true); break;
3579
            case FONT_BOLD_ITALIC:
3580
                ft.setBold(true);
3581
                ft.setItalic(true);
3582
            break;
3583
 
3584
            default:
3585
                ft.setBold(false);
3586
                ft.setItalic(false);
3587
        }
3588
 
3589
        QPalette palette;
3590
        TColor::COLOR_T textColor = TColor::getAMXColor(button->getTextColor());
3591
        TColor::COLOR_T fillColor = TColor::getAMXColor(button->getFillColor());
3592
        QColor txcolor(QColor::fromRgba(qRgba(textColor.red, textColor.green, textColor.blue, textColor.alpha)));
3593
        QColor cfcolor(QColor::fromRgba(qRgba(fillColor.red, fillColor.green, fillColor.blue, fillColor.alpha)));
3594
        palette.setColor(QPalette::Base, cfcolor);
3595
        palette.setColor(QPalette::Text, txcolor);
217 andreas 3596
//        pix.save("frame.png");
200 andreas 3597
        palette.setBrush(QPalette::Base, QBrush(pix));
3598
 
3599
        obj->object.list->setFont(ft);
3600
        obj->object.list->setPalette(palette);
3601
        // Add content
3602
        if (!listContent.empty())
3603
        {
3604
            vector<string>::iterator iter;
3605
 
205 andreas 3606
            if (obj->object.list->count() > 0)
3607
                obj->object.list->clear();
3608
 
3609
            MSG_DEBUG("Adding " << listContent.size() << " entries to list.");
3610
            string selected = gPageManager->getSelectedItem(handle);
3611
            int index = 0;
3612
 
200 andreas 3613
            for (iter = listContent.begin(); iter != listContent.end(); ++iter)
205 andreas 3614
            {
200 andreas 3615
                obj->object.list->addItem(iter->c_str());
205 andreas 3616
 
3617
                if (selected == *iter)
3618
                    obj->object.list->setCurrentRow(index);
3619
 
3620
                index++;
3621
            }
200 andreas 3622
        }
205 andreas 3623
        else
3624
            MSG_DEBUG("No items for list!");
200 andreas 3625
 
3626
        obj->object.list->show();
3627
    }
3628
    else
3629
        MSG_DEBUG("Object " << TObject::handleToString(handle) << " of type " << gObject->objectToString(obj->type) << " found!");
3630
}
3631
 
63 andreas 3632
void MainWindow::showKeyboard(const std::string& init, const std::string& prompt, bool priv)
31 andreas 3633
{
63 andreas 3634
    DECL_TRACER("MainWindow::showKeyboard(std::string &init, std::string &prompt, bool priv)");
31 andreas 3635
 
63 andreas 3636
    if (mKeyboard)
3637
        return;
3638
 
3639
    mQKeyboard = new TQKeyboard(init, prompt, this);
3640
    mKeyboard = true;
62 andreas 3641
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
65 andreas 3642
    mQKeyboard->setScaleFactor(mScaleFactor);
62 andreas 3643
#endif
63 andreas 3644
    mQKeyboard->setPrivate(priv);
3645
    mQKeyboard->doResize();
3646
    mQKeyboard->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
3647
    int ret = mQKeyboard->exec();
120 andreas 3648
    string text = "KEYB-";
31 andreas 3649
 
62 andreas 3650
    if (ret == QDialog::Accepted)
63 andreas 3651
        text.append(mQKeyboard->getText());
62 andreas 3652
    else
120 andreas 3653
        text = "KEYB-ABORT";
62 andreas 3654
 
120 andreas 3655
    if (gPageManager)
3656
        gPageManager->sendKeyboard(text);
62 andreas 3657
 
63 andreas 3658
    delete mQKeyboard;
3659
    mQKeyboard = nullptr;
3660
    mKeyboard = false;
31 andreas 3661
}
62 andreas 3662
 
63 andreas 3663
void MainWindow::showKeypad(const std::string& init, const std::string& prompt, bool priv)
62 andreas 3664
{
63 andreas 3665
    DECL_TRACER("MainWindow::showKeypad(std::string& init, std::string& prompt, bool priv)");
62 andreas 3666
 
65 andreas 3667
    if (mKeypad)
63 andreas 3668
        return;
3669
 
3670
    mQKeypad = new TQKeypad(init, prompt, this);
65 andreas 3671
    mKeypad = true;
62 andreas 3672
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
63 andreas 3673
    mQKeypad->setScaleFactor(mScaleFactor);
65 andreas 3674
#endif
3675
    mQKeypad->setPrivate(priv);
111 andreas 3676
    mQKeypad->setMaxLength(50);     // Standard maximum length
63 andreas 3677
    mQKeypad->doResize();
3678
    mQKeypad->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
65 andreas 3679
    int ret = mQKeypad->exec();
62 andreas 3680
 
3681
    if (ret == QDialog::Accepted)
3682
    {
3683
        string text = "KEYP-";
63 andreas 3684
        text.append(mQKeypad->getText());
62 andreas 3685
 
3686
        if (gPageManager)
3687
            gPageManager->sendKeypad(text);
3688
    }
3689
    else
3690
    {
3691
        string text = "KEYP-ABORT";
3692
 
3693
        if (gPageManager)
3694
            gPageManager->sendKeypad(text);
3695
    }
3696
 
63 andreas 3697
    delete mQKeypad;
3698
    mQKeypad = nullptr;
65 andreas 3699
    mKeypad = false;
62 andreas 3700
}
3701
 
63 andreas 3702
void MainWindow::resetKeyboard()
3703
{
3704
    DECL_TRACER("MainWindow::resetKeyboard()");
3705
 
3706
    if (mQKeyboard)
3707
        mQKeyboard->reject();
3708
 
3709
    if (mQKeypad)
211 andreas 3710
        mQKeypad->reject();
63 andreas 3711
}
3712
 
111 andreas 3713
void MainWindow::sendVirtualKeys(const string& str)
3714
{
3715
    DECL_TRACER("MainWindow::sendVirtualKeys(const string& str)");
3716
 
3717
    if (mKeyboard && mQKeyboard)
3718
        mQKeyboard->setString(str);
3719
    else if (mKeypad && mQKeypad)
3720
        mQKeypad->setString(str);
3721
}
3722
 
64 andreas 3723
void MainWindow::showSetup()
3724
{
3725
    DECL_TRACER("MainWindow::showSetup()");
251 andreas 3726
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
211 andreas 3727
    settings();
3728
#else
250 andreas 3729
#ifndef Q_OS_IOS
197 andreas 3730
    if (gPageManager)
3731
        gPageManager->showSetup();
250 andreas 3732
#else
3733
    QASettings::openSettings();
3734
#endif  // Q_OS_IOS
251 andreas 3735
#endif  // !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
64 andreas 3736
}
3737
 
71 andreas 3738
void MainWindow::playSound(const string& file)
3739
{
3740
    DECL_TRACER("MainWindow::playSound(const string& file)");
3741
 
3742
    MSG_DEBUG("Playing file " << file);
141 andreas 3743
 
3744
    if (TConfig::getMuteState())
3745
        return;
3746
 
3747
    if (!mMediaPlayer)
181 andreas 3748
    {
141 andreas 3749
        mMediaPlayer = new QMediaPlayer;
181 andreas 3750
#ifdef QT6_LINUX
3751
        mAudioOutput = new QAudioOutput;
3752
#endif
3753
    }
141 andreas 3754
 
181 andreas 3755
#ifdef QT5_LINUX
141 andreas 3756
    mMediaPlayer->setMedia(QUrl::fromLocalFile(file.c_str()));
3757
    mMediaPlayer->setVolume(calcVolume(TConfig::getSystemVolume()));
181 andreas 3758
#else
3759
    mMediaPlayer->setSource(QUrl::fromLocalFile(file.c_str()));
3760
    mAudioOutput->setVolume(TConfig::getSystemVolume());
3761
#endif
141 andreas 3762
    mMediaPlayer->play();
71 andreas 3763
}
3764
 
141 andreas 3765
void MainWindow::stopSound()
3766
{
3767
    DECL_TRACER("MainWindow::stopSound()");
3768
 
3769
    if (mMediaPlayer)
3770
        mMediaPlayer->stop();
3771
}
3772
 
3773
void MainWindow::muteSound(bool state)
3774
{
3775
    DECL_TRACER("MainWindow::muteSound(bool state)");
3776
 
181 andreas 3777
#ifdef QT5_LINUX
141 andreas 3778
    if (mMediaPlayer)
3779
        mMediaPlayer->setMuted(state);
181 andreas 3780
#else
3781
    if (mAudioOutput)
3782
        mAudioOutput->setMuted(state);
3783
#endif
141 andreas 3784
}
3785
 
31 andreas 3786
void MainWindow::playShowList()
3787
{
3788
    DECL_TRACER("MainWindow::playShowList()");
3789
 
61 andreas 3790
    _EMIT_TYPE_t etype = getNextType();
3791
 
3792
    while (etype != ET_NONE)
3793
    {
3794
        ulong handle = 0;
3795
        ulong parent = 0;
3796
        unsigned char *buffer;
3797
        int pixline = 0;
3798
        int left = 0;
3799
        int top = 0;
3800
        int width = 0;
3801
        int height = 0;
192 andreas 3802
        int frame = 0;
61 andreas 3803
        unsigned char *image;
3804
        size_t size = 0;
3805
        size_t rowBytes = 0;
3806
        ulong color = 0;
3807
        ANIMATION_t animate;
3808
        std::string url;
3809
        std::string user;
3810
        std::string pw;
3811
        Button::TButton *button;
3812
        Button::BITMAP_t bm;
3813
 
3814
        switch(etype)
3815
        {
3816
            case ET_BACKGROUND:
3817
                if (getBackground(&handle, &image, &size, &rowBytes, &width, &height, &color))
3818
                {
3819
                    QByteArray buf;
3820
 
3821
                    if (image && size > 0)
3822
                        buf.insert(0, (const char *)image, size);
3823
 
217 andreas 3824
                    MSG_PROTOCOL("Replay: BACKGROUND of object " << TObject::handleToString(handle));
61 andreas 3825
                    emit sigSetBackground(handle, buf, rowBytes, width, height, color);
3826
                }
3827
            break;
3828
 
3829
            case ET_BUTTON:
3830
                if (getButton(&handle, &parent, &buffer, &pixline, &left, &top, &width, &height))
3831
                {
3832
                    QByteArray buf;
3833
 
3834
                    if (buffer && pixline > 0)
3835
                    {
3836
                        size_t size = width * height * (pixline / width);
3837
                        MSG_DEBUG("Buffer size=" << size << ", width=" << width << ", height=" << height << ", left=" << left << ", top=" << top);
3838
                        buf.insert(0, (const char *)buffer, size);
3839
                    }
3840
 
217 andreas 3841
                    MSG_PROTOCOL("Replay: BUTTON object " << TObject::handleToString(handle));
61 andreas 3842
                    emit sigDisplayButton(handle, parent, buf, width, height, pixline, left, top);
3843
                }
3844
            break;
3845
 
3846
            case ET_INTEXT:
192 andreas 3847
                if (getInText(&handle, &button, &bm, &frame))
61 andreas 3848
                {
3849
                    QByteArray buf;
3850
 
3851
                    if (bm.buffer && bm.rowBytes > 0)
3852
                    {
3853
                        size_t size = bm.width * bm.height * (bm.rowBytes / bm.width);
3854
                        buf.insert(0, (const char *)bm.buffer, size);
3855
                    }
3856
 
217 andreas 3857
                    MSG_PROTOCOL("Replay: INTEXT object " << TObject::handleToString(handle));
192 andreas 3858
                    emit sigInputText(button, buf, bm.width, bm.height, bm.rowBytes, frame);
61 andreas 3859
                }
3860
            break;
3861
 
3862
            case ET_PAGE:
3863
                if (getPage(&handle, &width, &height))
3864
                {
217 andreas 3865
                    MSG_PROTOCOL("Replay: PAGE object " << TObject::handleToString(handle));
3866
 
61 andreas 3867
                    if (isDeleted())
3868
                        emit sigDropPage(handle);
3869
                    else
3870
                        emit sigSetPage(handle, width, height);
3871
                }
3872
            break;
3873
 
3874
            case ET_SUBPAGE:
217 andreas 3875
                if (getSubPage(&handle, &parent, &left, &top, &width, &height, &animate))
61 andreas 3876
                {
217 andreas 3877
                    MSG_PROTOCOL("Replay: SUBPAGE object " << TObject::handleToString(handle));
3878
 
61 andreas 3879
                    if (isDeleted())
3880
                        emit sigDropSubPage(handle);
3881
                    else
217 andreas 3882
                        emit sigSetSubPage(handle, parent, left, top, width, height, animate);
61 andreas 3883
                }
3884
            break;
3885
 
3886
            case ET_VIDEO:
3887
                if (getVideo(&handle, &parent, &left, &top, &width, &height, &url, &user, &pw))
3888
                {
217 andreas 3889
                    MSG_PROTOCOL("Replay: VIDEO object " << TObject::handleToString(handle));
61 andreas 3890
                    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
3891
                }
3892
            break;
3893
 
3894
            default:
3895
                MSG_WARNING("Type " << etype << " is currently not supported!");
3896
        }
3897
 
3898
        dropType(etype);
3899
        etype = getNextType();
3900
    }
3901
/*
31 andreas 3902
    std::map<ulong, QWidget *>::iterator iter;
3903
 
3904
    for (iter = mToShow.begin(); iter != mToShow.end(); ++iter)
3905
    {
3906
        OBJECT_t *obj = findObject(iter->first);
3907
 
3908
        if (obj)
3909
            iter->second->show();
3910
    }
3911
 
3912
    mToShow.clear();
61 andreas 3913
*/
31 andreas 3914
}
3915
 
42 andreas 3916
 
38 andreas 3917
int MainWindow::scale(int value)
3918
{
3919
    if (value <= 0 || mScaleFactor == 1.0)
3920
        return value;
3921
 
3922
    return (int)((double)value * mScaleFactor);
3923
}
3924
 
198 andreas 3925
int MainWindow::scaleSetup(int value)
3926
{
3927
    DECL_TRACER("MainWindow::scaleSetup(int value)");
3928
 
212 andreas 3929
    if (value <= 0 || mSetupScaleFactor == 1.0 || mSetupScaleFactor <= 0.0)
198 andreas 3930
        return value;
3931
 
3932
    double val = (double)value * mSetupScaleFactor;
3933
 
3934
    return (int)val;
3935
}
3936
 
3937
bool MainWindow::isSetupScaled()
3938
{
3939
    DECL_TRACER("MainWindow::isSetupScaled()");
3940
 
3941
    return (mSetupScaleFactor > 0.0 && mSetupScaleFactor != 1.0);
3942
}
3943
 
107 andreas 3944
void MainWindow::startAnimation(TObject::OBJECT_t* obj, ANIMATION_t& ani, bool in)
42 andreas 3945
{
3946
    DECL_TRACER("MainWindow::startAnimation(OBJECT_t* obj, ANIMATION_t& ani)");
43 andreas 3947
 
107 andreas 3948
    if (!obj)
3949
    {
3950
        MSG_ERROR("Got no object to start the animation!");
3951
        return;
3952
    }
3953
 
42 andreas 3954
    SHOWEFFECT_t effect;
3955
    int scLeft = obj->left;
3956
    int scTop = obj->top;
3957
    int scWidth = obj->width;
3958
    int scHeight = obj->height;
3959
    mLastObject = nullptr;
43 andreas 3960
 
42 andreas 3961
    obj->animate = ani;
43 andreas 3962
 
42 andreas 3963
    if (in)
3964
        effect = ani.showEffect;
3965
    else
3966
        effect = ani.hideEffect;
3967
 
54 andreas 3968
    if (effect == SE_NONE)
3969
        return;
43 andreas 3970
 
58 andreas 3971
    if (effect == SE_FADE)
3972
    {
107 andreas 3973
        MSG_DEBUG("Fading object " << TObject::handleToString(obj->handle) << (in ? " IN" : " OUT"));
58 andreas 3974
        QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(obj->object.widget);
3975
        obj->object.widget->setGraphicsEffect(effect);
3976
        obj->animation = new QPropertyAnimation(effect, "opacity");
3977
    }
3978
    else
3979
    {
107 andreas 3980
        MSG_DEBUG("Moving object " << TObject::handleToString(obj->handle) << (in ? " IN" : " OUT"));
58 andreas 3981
        obj->animation = new QPropertyAnimation(obj->object.widget);
3982
        obj->animation->setTargetObject(obj->object.widget);
3983
    }
42 andreas 3984
 
54 andreas 3985
    if (in)
101 andreas 3986
        obj->animation->setDuration(ani.showTime * 100);    // convert 10th of seconds into milliseconds
54 andreas 3987
    else
101 andreas 3988
        obj->animation->setDuration(ani.hideTime * 100);    // convert 10th of seconds into milliseconds
43 andreas 3989
 
54 andreas 3990
    switch(effect)
3991
    {
3992
        case SE_SLIDE_BOTTOM_FADE:
3993
        case SE_SLIDE_BOTTOM:
3994
            obj->animation->setPropertyName("geometry");
42 andreas 3995
 
54 andreas 3996
            if (in)
3997
            {
3998
                obj->animation->setStartValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
3999
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
4000
            }
4001
            else
4002
            {
4003
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
4004
                obj->animation->setEndValue(QRect(scLeft, scTop + (scHeight * 2), scWidth, scHeight));
4005
                obj->remove = true;
4006
                mLastObject = obj;
4007
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
4008
            }
42 andreas 4009
 
54 andreas 4010
            obj->animation->start();
4011
        break;
43 andreas 4012
 
54 andreas 4013
        case SE_SLIDE_LEFT_FADE:
4014
        case SE_SLIDE_LEFT:
4015
            obj->animation->setPropertyName("geometry");
43 andreas 4016
 
54 andreas 4017
            if (in)
4018
            {
4019
                obj->animation->setStartValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
4020
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
4021
            }
4022
            else
4023
            {
4024
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
4025
                obj->animation->setEndValue(QRect(scLeft - scWidth, scTop, scWidth, scHeight));
4026
                obj->remove = true;
4027
                mLastObject = obj;
4028
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
4029
            }
42 andreas 4030
 
54 andreas 4031
            obj->animation->start();
4032
        break;
43 andreas 4033
 
54 andreas 4034
        case SE_SLIDE_RIGHT_FADE:
4035
        case SE_SLIDE_RIGHT:
4036
            obj->animation->setPropertyName("geometry");
43 andreas 4037
 
54 andreas 4038
            if (in)
4039
            {
4040
                obj->animation->setStartValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
4041
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
4042
            }
4043
            else
4044
            {
4045
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
4046
                obj->animation->setEndValue(QRect(scLeft + scWidth, scTop, scWidth, scHeight));
4047
                obj->remove = true;
4048
                mLastObject = obj;
4049
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
4050
            }
42 andreas 4051
 
54 andreas 4052
            obj->animation->start();
4053
        break;
43 andreas 4054
 
54 andreas 4055
        case SE_SLIDE_TOP_FADE:
4056
        case SE_SLIDE_TOP:
4057
            obj->animation->setPropertyName("geometry");
43 andreas 4058
 
54 andreas 4059
            if (in)
4060
            {
4061
                obj->animation->setStartValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
4062
                obj->animation->setEndValue(QRect(scLeft, scTop, scWidth, scHeight));
4063
            }
4064
            else
4065
            {
4066
                obj->animation->setStartValue(QRect(scLeft, scTop, scWidth, scHeight));
4067
                obj->animation->setEndValue(QRect(scLeft, scTop - scHeight, scWidth, scHeight));
4068
                obj->remove = true;
4069
                mLastObject = obj;
4070
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
4071
            }
43 andreas 4072
 
54 andreas 4073
            obj->animation->start();
4074
        break;
4075
 
58 andreas 4076
        case SE_FADE:
4077
            if (in)
4078
            {
4079
                obj->object.widget->setWindowOpacity(0.0);
4080
                obj->object.widget->show();
4081
                obj->animation->setStartValue(0.0);
4082
                obj->animation->setEndValue(1.0);
4083
            }
4084
            else
4085
            {
4086
                obj->animation->setStartValue(1.0);
4087
                obj->animation->setEndValue(0.0);
4088
                obj->remove = true;
4089
                mLastObject = obj;
4090
                connect(obj->animation, &QPropertyAnimation::finished, this, &MainWindow::animationFinished);
4091
            }
4092
 
4093
            obj->animation->setEasingCurve(QEasingCurve::Linear);
4094
            obj->animation->start();
4095
        break;
4096
 
54 andreas 4097
        default:
4098
            MSG_WARNING("Subpage effect " << ani.showEffect << " is not supported.");
42 andreas 4099
    }
4100
}
4101
 
179 andreas 4102
void MainWindow::downloadBar(const string &msg, QWidget *parent)
4103
{
4104
    DECL_TRACER("void MainWindow::downloadBar(const string &msg, QWidget *parent)");
4105
 
4106
    if (mBusy)
4107
        return;
4108
 
4109
    mBusy = true;
4110
    mDownloadBar = new TqDownload(msg, parent);
4111
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
4112
    mDownloadBar->setScaleFactor(gScale);
4113
    mDownloadBar->doResize();
4114
#endif
4115
    mDownloadBar->show();
4116
}
4117
 
120 andreas 4118
void MainWindow::runEvents()
4119
{
4120
    DECL_TRACER("MainWindow::runEvents()");
4121
 
4122
    QApplication::processEvents();
4123
}
4124
 
2 andreas 4125
#ifndef QT_NO_SESSIONMANAGER
4126
void MainWindow::commitData(QSessionManager &manager)
4127
{
5 andreas 4128
    if (manager.allowsInteraction())
4129
    {
4130
        if (!settingsChanged)
4131
            manager.cancel();
4132
    }
4133
    else
4134
    {
4135
        if (settingsChanged)
4136
            writeSettings();
4137
    }
2 andreas 4138
}
5 andreas 4139
#endif