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