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