Subversion Repositories tpanel

Rev

Rev 19 | Blame | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

/** @file tqtmain.cpp
 * @brief Implements the surface of the application.
 *
 * This file implements the callback functions of the suface. While the most
 * classes are drawing the elements, the methods here take the ready elements
 * and display them. This file makes the surface completely independent of
 * the rest of the application which makes it easy to change the surface by
 * any other technology.
 */
#include <QApplication>
#include <QByteArray>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QLabel>
#include <QtWidgets>
#include <QMouseEvent>
#include <QMoveEvent>
#include <QPalette>
#include <QPixmap>
#include <QFont>
#include <QFontDatabase>
#include <QtMultimediaWidgets/QVideoWidget>
#include <QtMultimedia/QMediaPlayer>
#include <QtMultimedia/QMediaPlaylist>
#include <QUrl>
#include <QThread>

#include <functional>
#include <mutex>

#include "tpagemanager.h"
#include "tqtmain.h"
#include "tconfig.h"
#include "tqtsettings.h"
#include "tcolor.h"

/**
 * @def __ANDROID__
 * Here we've to define some extra place holders because on Android only 10
 * place holders are defined by default. But we've a callback functions which
 * need 13 place holders. The defination is taken from the original defination
 * of place holders and expanded.
 */
#ifdef __ANDROID__
#if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY)
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _11;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _12;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _13;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _14;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _15;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _16;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _17;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _18;
_LIBCPP_FUNC_VIS extern const std::placeholders::__ph<10> _19;
_LIBCPP_FUNC_VIS extern const _std::placeholders::_ph<10> _20;
#else
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<11>   _11{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<12>   _12{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<13>   _13{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<14>   _14{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<15>   _15{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<16>   _16{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<17>   _17{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<18>   _18{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<19>   _19{};
/* _LIBCPP_INLINE_VAR */ constexpr std::placeholders::__ph<20>   _20{};
#endif // defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY)
#endif

static TPageManager *pageManager = nullptr;     //!< The pointer to the global defined main class.
std::mutex draw_mutex;                          //!< We're using threads and need to block execution sometimes

using std::bind;
using std::string;

/**
 * @brief qtmain is used here as the entry point for the surface.
 *
 * The main entry function parses the command line parameters, if there were
 * any and sets the basic attributes. It creates the main window and starts the
 * application.
 *
 * @param argc      The number of command line arguments
 * @param argv      A pointer to a 2 dimensional array containing the command
 *                  line parameters.
 * @param pmanager  A pointer to the page manager class which is the main class
 *                  managing everything.
 *
 * @return If no errors occured it returns 0.
 */
int qtmain(int argc, char **argv, TPageManager *pmanager)
{
    DECL_TRACER("qtmain(int argc, char **argv, width, int height)");

    pageManager = pmanager;

//    Q_INIT_RESOURCE(tpanel);
#ifdef Q_OS_ANDROID
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QApplication app(argc, argv);
    QCoreApplication::setOrganizationName(TConfig::getProgName().c_str());
    QCoreApplication::setApplicationName("AMX panel simulator");
    QCoreApplication::setApplicationVersion(QT_VERSION_STR);
    QCommandLineParser parser;
    parser.setApplicationDescription(QCoreApplication::applicationName());
    parser.addHelpOption();
    parser.addVersionOption();
    parser.process(app);

    MainWindow mainWin;
    mainWin.show();
    return app.exec();
}

/**
 * @brief MainWindow::MainWindow constructor
 *
 * This method is the constructor for this class. It registers the callback
 * functions to the class TPageManager and starts the main run loop.
 */
MainWindow::MainWindow()
{
    DECL_TRACER("MainWindow::MainWindow()");

    pageManager->registerCallbackDB(bind(&MainWindow::_displayButton, this,
                                       std::placeholders::_1,
                                       std::placeholders::_2,
                                       std::placeholders::_3,
                                       std::placeholders::_4,
                                       std::placeholders::_5,
                                       std::placeholders::_6,
                                       std::placeholders::_7,
                                       std::placeholders::_8));

    pageManager->registerCallbackSP(bind(&MainWindow::_setPage, this,
                                         std::placeholders::_1,
                                         std::placeholders::_2,
                                         std::placeholders::_3));

    pageManager->registerCallbackSSP(bind(&MainWindow::_setSubPage, this,
                                          std::placeholders::_1,
                                          std::placeholders::_2,
                                          std::placeholders::_3,
                                          std::placeholders::_4,
                                          std::placeholders::_5));

    pageManager->registerCallbackSB(bind(&MainWindow::_setBackground, this,
                                         std::placeholders::_1,
                                         std::placeholders::_2,
                                         std::placeholders::_3,
                                         std::placeholders::_4,
                                         std::placeholders::_5));

    pageManager->registerCallbackFT(bind(&MainWindow::_setText, this,
                                         std::placeholders::_1,
                                         std::placeholders::_2,
                                         std::placeholders::_3,
                                         std::placeholders::_4,
                                         std::placeholders::_5,
                                         std::placeholders::_6,
                                         std::placeholders::_7,
                                         std::placeholders::_8,
                                         std::placeholders::_9,
                                         std::placeholders::_10,
#ifdef __ANDROID__
                                         _11,
                                         _12,
                                         _13));
#else
                                         std::placeholders::_11,
                                         std::placeholders::_12,
                                         std::placeholders::_13));
#endif

    pageManager->regCallDropPage(bind(&MainWindow::_dropPage, this, std::placeholders::_1));
    pageManager->regCallDropSubPage(bind(&MainWindow::_dropSubPage, this, std::placeholders::_1));
    pageManager->regCallPlayVideo(bind(&MainWindow::_playVideo, this,
                                       std::placeholders::_1,
                                       std::placeholders::_2,
                                       std::placeholders::_3,
                                       std::placeholders::_4,
                                       std::placeholders::_5,
                                       std::placeholders::_6,
                                       std::placeholders::_7,
                                       std::placeholders::_8,
                                       std::placeholders::_9));

    createActions();

#ifndef QT_NO_SESSIONMANAGER
    QGuiApplication::setFallbackSessionManagementEnabled(false);
    connect(qApp, &QGuiApplication::commitDataRequest,
            this, &MainWindow::writeSettings);
#endif

    qRegisterMetaType<size_t>("size_t");
    qRegisterMetaType<QByteArray>("QByteArray");

    try
    {
        connect(this, &MainWindow::sigDisplayButton, this, &MainWindow::displayButton);
        connect(this, &MainWindow::sigDisplayButton, // same sender and signal
                this,                 // context object to break this connection
                [this]() {            // debug output
                    MSG_DEBUG("Direct? " << ((QThread::currentThread() == this->thread())?"YES":"NO"));
                },
                Qt::DirectConnection);
        connect(this, &MainWindow::sigSetPage, this, &MainWindow::setPage);
        connect(this, &MainWindow::sigSetSubPage, this, &MainWindow::setSubPage);
        connect(this, &MainWindow::sigSetBackground, this, &MainWindow::setBackground);
        connect(this, &MainWindow::sigSetText, this, &MainWindow::setText);
        connect(this, &MainWindow::sigDropPage, this, &MainWindow::dropPage);
        connect(this, &MainWindow::sigDropSubPage, this, &MainWindow::dropSubPage);
        connect(this, &MainWindow::sigPlayVideo, this, &MainWindow::playVideo);
    }
    catch (std::exception& e)
    {
        MSG_ERROR("Connection error: " << e.what());
    }

    setUnifiedTitleAndToolBarOnMac(true);
    pageManager->run();
}

/**
 * @brief MainWindow::closeEvent called when the application receives an exit event.
 *
 * If the user clicks on the exit icon or on the menu point _Exit_ this method
 * is called. It makes sure everything is written to the configuration file
 * and accepts the evernt.
 *
 * @param event The exit event
 */
void MainWindow::closeEvent(QCloseEvent *event)
{
    DECL_TRACER("MainWindow::closeEvent(QCloseEvent *event)");

    if (settingsChanged)
    {
        writeSettings();
        event->accept();
    }
    else
    {
//      event->ignore();
        prg_stopped = true;
        MSG_TRACE("Program will stop!");
        event->accept();
    }
}

/**
 * @brief MainWindow::eventFilter filters the QEvent::MouseMove event
 * @param obj   The object where the event occured.
 * @param event The event.
 * @return `true` when the event should be ignored.
 */
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
//    DECL_TRACER("MainWindow::eventFilter(QObject *obj, QEvent *event)");

    if (event->type() == QEvent::MouseMove)
        return true;    // Filter event out, i.e. stop it being handled further.

    return QMainWindow::eventFilter(obj, event);
}

/**
 * @brief MainWindow::createActions creates the configuration dialog window.
 */
void MainWindow::createActions()
{
    DECL_TRACER("MainWindow::createActions()");

    // Add a menu
    QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));

    const QIcon settingsIcon = QIcon::fromTheme("document-open", QIcon(":/images/settings-configure.png"));
    QAction *settingsAct = new QAction(settingsIcon, tr("&Settings..."), this);
    settingsAct->setStatusTip(tr("Change the settings"));
    connect(settingsAct, &QAction::triggered, this, &MainWindow::settings);
    editMenu->addAction(settingsAct);

    const QIcon aboutIcon = QIcon::fromTheme("document-open", QIcon(":/images/help-about.png"));
    QAction *aboutAct = new QAction(aboutIcon, tr("&About..."), this);
    aboutAct->setShortcuts(QKeySequence::Open);
    aboutAct->setStatusTip(tr("About this program"));
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
    editMenu->addAction(aboutAct);

    editMenu->addSeparator();

    const QIcon exitIcon = QIcon::fromTheme("application-exit");
    QAction *exitAct = editMenu->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
    exitAct->setShortcuts(QKeySequence::Quit);
    exitAct->setStatusTip(tr("Exit the application"));
}

/**
 * @brief MainWindow::mousePressEvent catches the event Qt::LeftButton.
 *
 * If the user presses the left mouse button somewhere in the main window, this
 * method is triggered. It retrieves the position of the mouse pointer and
 * sends it to the page manager TPageManager.
 *
 * @param event The event
 */
void MainWindow::mousePressEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        pageManager->mouseEvent(event->x(), event->y(), true);
    }
}

/**
 * @brief MainWindow::mouseReleaseEvent catches the event Qt::LeftButton.
 *
 * If the user releases the left mouse button somewhere in the main window, this
 * method is triggered. It retrieves the position of the mouse pointer and
 * sends it to the page manager TPageManager.
 *
 * @param event The event
 */
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        pageManager->mouseEvent(event->x(), event->y(), false);
    }
}

/**
 * @brief MainWindow::settings initiates the configuration dialog.
 */
void MainWindow::settings()
{
    DECL_TRACER("MainWindow::settings()");

    TQtSettings *dlg_settings = new TQtSettings(this);
    dlg_settings->show();
}

/**
 * @brief MainWindow::writeSettings Writes the settings into the configuration file.
 */
void MainWindow::writeSettings()
{
    DECL_TRACER("MainWindow::writeSettings()");
}

/**
 * @brief MainWindow::about displays the _about_ dialog.
 */
void MainWindow::about()
{
    DECL_TRACER("MainWindow::about()");

    QMessageBox::about(this, tr(std::string("About ").append(TConfig::getProgName()).c_str()),
                       tr("Simulation of an AMX G4 panel\n"
                          "(C) Copyright 2020 by Andreas Theofilu <andreas@theosys.at>\n"
                          "This program is under the terms of GPL version 3"));
}

/******************* Signal handling *************************/

void MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)
{
    DECL_TRACER("MainWindow::_displayButton(ulong handle, ulong parent, unsigned char* buffer, int width, int height, int pixline, int left, int top)");

    if (prg_stopped)
        return;

    QByteArray buf;

    if (buffer && pixline > 0)
    {
        size_t size = width * height * (pixline / width);
        MSG_DEBUG("Buffer size=" << size << ", width=" << width << ", height=" << height << ", left=" << left << ", top=" << top);
        buf.insert(0, (const char *)buffer, size);
    }

    try
    {
        emit sigDisplayButton(handle, parent, buf, width, height, pixline, left, top);
    }
    catch (std::exception& e)
    {
        MSG_ERROR("Error triggering function \"displayButton()\": " << e.what());
    }
}

void MainWindow::_setPage(ulong handle, int width, int height)
{
    DECL_TRACER("MainWindow::_setPage(ulong handle, int width, int height)");

    if (prg_stopped)
        return;

    emit sigSetPage(handle, width, height);
}

void MainWindow::_setSubPage(ulong handle, int left, int top, int width, int height)
{
    DECL_TRACER("MainWindow::_setSubPage(ulong handle, int left, int top, int width, int height)");

    if (prg_stopped)
        return;

    emit sigSetSubPage(handle, left, top, width, height);
}

void MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, ulong color)
{
    DECL_TRACER("MainWindow::_setBackground(ulong handle, unsigned char *image, size_t size, size_t rowBytes, ulong color)");

    if (prg_stopped)
        return;

    QByteArray buf;

    if (image && size > 0)
        buf.insert(0, (const char *)image, size);

    emit sigSetBackground(handle, buf, rowBytes, color);
}

void MainWindow::_setText(ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, Button::TEXT_ORIENTATION ori, Button::TEXT_EFFECT effect, bool ww)
{
    DECL_TRACER("MainWindow::_setText(ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, Button::TEXT_ORIENTATION ori, Button::TEXT_EFFECT effect, bool ww)");

    if (prg_stopped)
        return;

    emit sigSetText(handle, text, font, family, size, x, y, color, effectColor, style, ori, effect, ww);
}

void MainWindow::_dropPage(ulong handle)
{
    DECL_TRACER("MainWindow::_dropPage(ulong handle)");

    emit sigDropPage(handle);
}

void MainWindow::_dropSubPage(ulong handle)
{
    DECL_TRACER("MainWindow::_dropSubPage(ulong handle)");

    emit sigDropSubPage(handle);
}

void MainWindow::_playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
{
    DECL_TRACER("MainWindow::_playVideo(ulong handle, const string& url)");

    if (prg_stopped)
        return;

    emit sigPlayVideo(handle, parent, left, top, width, height, url, user, pw);
}

/******************* Draw elements *************************/

void MainWindow::displayButton(ulong handle, ulong parent, QByteArray buffer, int width, int height, int pixline, int left, int top)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::displayButton(ulong handle, unsigned char* buffer, size_t size, int width, int height, int pixline, int left, int top)");

    OBJECT_t *obj = findObject(handle);
    OBJECT_t *par = findObject(parent);
    MSG_DEBUG("Processing button " << handleToString(handle) << " from parent " << handleToString(parent));

    if (!par)
    {
        MSG_WARNING("Button " << handle << " has no parent (" << parent << ")! Ignoring it.");
        draw_mutex.unlock();
        return;
    }

    if (!obj)
    {
        MSG_DEBUG("Adding new object ...");
        obj = addObject();

        if (!obj)
        {
            MSG_ERROR("Error creating an object!");
            TError::setError();
            draw_mutex.unlock();
            return;
        }

        obj->type = OBJ_BUTTON;
        obj->handle = handle;
        obj->width = width;
        obj->height = height;
        obj->left = left;
        obj->top = top;
        obj->object.label = new QLabel("", par->object.widget);
        obj->object.label->installEventFilter(this);
    }
    else
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");

    obj->object.label->setFixedSize(width, height);
    obj->object.label->move(left, top);
    obj->object.label->setAttribute(Qt::WA_TransparentForMouseEvents);
    MSG_DEBUG("Geometry: l:" << left << ", t:" << top << ", w:" << width << ", h:" << height);

    if (buffer.size() > 0 && pixline > 0)
    {
        MSG_DEBUG("Adding image for " << handleToString(handle) << " ...");
        QImage img((unsigned char *)buffer.data(), width, height, pixline, QImage::Format_ARGB32);
        QPixmap pixmap(width, height);
        pixmap.convertFromImage(img);
        obj->object.label->setPixmap(pixmap);
    }
    else
    {
        MSG_DEBUG("No image found.");
    }

    obj->object.label->show();
    draw_mutex.unlock();
}

void MainWindow::setPage(ulong handle, int width, int height)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::setPage(ulong handle, int width, int height)");

    QSize qs = menuBar()->sizeHint();
    this->setMinimumSize(width, height + qs.height());

    OBJECT_t *obj = findObject(handle);

    if (!obj)
    {
        obj = addObject();

        if (!obj)
        {
            MSG_ERROR("Error crating an object for handle " << handleToString(handle));
            TError::setError();
            draw_mutex.unlock();
            return;
        }

        obj->handle = handle;
        obj->height = height;
        obj->width = width;
        obj->type = OBJ_PAGE;
    }

    bool newBackground = false;

    if (!mBackground)
    {
        mBackground = new QWidget();
        mBackground->setAutoFillBackground(true);
        mBackground->setFixedSize(obj->width, obj->height);
        QRect rectBack = mBackground->geometry();
        QRect rectMain = this->geometry();
        pageManager->setFirstTopPixel(rectMain.height() - rectBack.height());
        newBackground = true;
    }

    // By default set a transparent background
    QPixmap pix(width, height);
    pix.fill(QColor::fromRgba(qRgba(0,0,0,0xff)));
    QPalette palette;
    palette.setBrush(QPalette::Window, pix);
    mBackground->setPalette(palette);

    if (newBackground)
        this->setCentralWidget(mBackground);

    mBackground->show();
    draw_mutex.unlock();
}

void MainWindow::setSubPage(ulong handle, int left, int top, int width, int height)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::setSubPage(ulong handle, int left, int top, int width, int height)");

    OBJECT_t *obj = addObject();

    if (!obj)
    {
        MSG_ERROR("Error adding an object!");
        TError::setError();
        draw_mutex.unlock();
        return;
    }

    obj->type = OBJ_SUBPAGE;
    obj->handle = handle;
    obj->object.widget = new QWidget(centralWidget());
    obj->object.widget->setAutoFillBackground(true);
    obj->object.widget->setFixedSize(width, height);
    obj->object.widget->move(left, top);
    obj->left = left;
    obj->top = top;
    obj->width = width;
    obj->height = height;
    // filter move event
    obj->object.widget->installEventFilter(this);
    // By default set a transparent background
    QPixmap pix(width, height);
    pix.fill(QColor::fromRgba(qRgba(0,0,0,0xff)));
    QPalette palette;
    palette.setBrush(QPalette::Window, QBrush(pix));
    obj->object.widget->setPalette(palette);
    draw_mutex.unlock();
}

void MainWindow::setBackground(ulong handle, QByteArray image, size_t rowBytes, ulong color)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::setBackground(ulong handle, unsigned char* image, size_t size, size_t rowBytes, ulong color)");

    OBJECT_t *obj = findObject(handle);

    if (!obj)
    {
        MSG_WARNING("No object " << handleToString(handle) << " found!");
        draw_mutex.unlock();
        return;
    }

    MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");

    if (obj->type == OBJ_BUTTON || obj->type == OBJ_SUBPAGE)
    {
        MSG_DEBUG("Processing object " << objectToString(obj->type));
        QPixmap pix(obj->width, obj->height);
        pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));

        if (image.size() > 0)
        {
            MSG_DEBUG("Setting image of size " << image.size());
            QImage img((unsigned char *)image.data(), obj->width, obj->height, rowBytes, QImage::Format_ARGB32);
            pix.convertFromImage(img);
        }

        if (obj->type == OBJ_BUTTON)
        {
            obj->object.label->setPixmap(pix);
            obj->object.label->show();
        }
        else
        {
            MSG_DEBUG("Setting image as background for page " << ((handle >> 16) & 0x0000ffff));
            QPalette palette;
            palette.setBrush(QPalette::Window, QBrush(pix));
            // FIXME: Background image is not visible!
            obj->object.widget->setPalette(palette);
            obj->object.widget->show();
        }
    }
    else if (obj->type == OBJ_PAGE)
    {
        bool newBackground = false;

        if (!mBackground)
        {
            mBackground = new QWidget();
            mBackground->setAutoFillBackground(true);
            mBackground->setFixedSize(obj->width, obj->height);
            newBackground = true;
            MSG_DEBUG("New background image added to page with size " << obj->width << " x " << obj->height);
        }

        QPixmap pix(obj->width, obj->height);
        pix.fill(QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));

        if (image.size() > 0)
        {
            QImage img((unsigned char *)image.data(), obj->width, obj->height, rowBytes, QImage::Format_ARGB32);
            pix.convertFromImage(img);
        }

        QPalette palette;
        palette.setBrush(QPalette::Window, QBrush(pix));
        mBackground->setPalette(palette);

        if (newBackground)
            this->setCentralWidget(mBackground);

        mBackground->show();
        MSG_DEBUG("Background set");
    }

    draw_mutex.unlock();
}

void MainWindow::setText(ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, Button::TEXT_ORIENTATION ori, Button::TEXT_EFFECT effect, bool ww)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::setText(ulong handle, const std::string& text, const std::string& font, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, Button::TEXT_ORIENTATION ori, Button::TEXT_EFFECT effect, bool ww)");

    OBJECT_t *obj = findObject(handle);

    if (!obj)
    {
        MSG_ERROR("Object " << handleToString(handle) << " not found!");
        TError::setError();
        draw_mutex.unlock();
        return;
    }

    if (!obj->object.label)
    {
        MSG_ERROR("Object " << handleToString(handle) << " contains no child element!");
        TError::setError();
        draw_mutex.unlock();
        return;
    }

    MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");

#if !defined(QT_DISABLE_DEPRECATED_BEFORE) || (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
    QPixmap pix = *obj->object.label->pixmap();
#else
    QPixmap pix = obj->object.label->pixmap(Qt::ReturnByValue);
#endif
    // Load the font
    int fontID = 0;

    if ((fontID = QFontDatabase::addApplicationFont(font.c_str())) == -1)
    {
        MSG_ERROR("Font " << font << " could not be loaded!");
        TError::setError();
        draw_mutex.unlock();
        return;
    }

    QFont ft;
    ft.setFamily(family.c_str());
    ft.setPointSize(size);

    switch (style)
    {
        case FONT_BOLD:     ft.setBold(true); break;
        case FONT_ITALIC:   ft.setItalic(true); break;
        case FONT_BOLD_ITALIC:
            ft.setBold(true);
            ft.setItalic(true);
        break;

        default:
            ft.setBold(false);
            ft.setItalic(false);
    }

    QPalette palette = obj->object.label->palette();
    palette.setColor(QPalette::WindowText, QColor::fromRgba(qRgba(TColor::getRed(color),TColor::getGreen(color),TColor::getBlue(color),TColor::getAlpha(color))));
    obj->object.label->setPalette(palette);
    obj->object.label->setFont(ft);
    obj->object.label->setWordWrap(ww);
    obj->object.label->setIndent(4);

    switch (ori)
    {
        case Button::ORI_ABSOLUT:
        {
            QString pos = "<font style=\"position: absolute;left:";
            pos.append(x);
            pos.append(";top:");
            pos.append(y);
            pos.append(";>");
            pos.append(text.c_str());
            pos.append("</font>");
            MSG_DEBUG("Text: " << pos.data());
        }
        break;

        case Button::ORI_TOP_LEFT:      obj->object.label->setAlignment(Qt::AlignTop | Qt::AlignLeft); break;
        case Button::ORI_TOP_MIDDLE:    obj->object.label->setAlignment(Qt::AlignTop | Qt::AlignHCenter); break;
        case Button::ORI_TOP_RIGHT:     obj->object.label->setAlignment(Qt::AlignTop | Qt::AlignRight); break;
        case Button::ORI_CENTER_LEFT:   obj->object.label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); break;
        case Button::ORI_CENTER_MIDDLE: obj->object.label->setAlignment(Qt::AlignCenter); break;
        case Button::ORI_CENTER_RIGHT:  obj->object.label->setAlignment(Qt::AlignVCenter | Qt::AlignRight); break;
        case Button::ORI_BOTTOM_LEFT:   obj->object.label->setAlignment(Qt::AlignBottom | Qt::AlignLeft); break;
        case Button::ORI_BOTTOM_MIDDLE: obj->object.label->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); break;
        case Button::ORI_BOTTOM_RIGHT:  obj->object.label->setAlignment(Qt::AlignBottom | Qt::AlignRight); break;
    }

    obj->object.label->setText(text.c_str());
    draw_mutex.unlock();
}

void MainWindow::dropPage(ulong handle)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::dropPage(ulong handle)");

    removeAllChilds(handle);
    removeObject(handle);

    if (mBackground)
    {
        delete mBackground;
        mBackground = nullptr;
    }

    draw_mutex.unlock();
}

void MainWindow::dropSubPage(ulong handle)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::dropSubPage(ulong handle)");

    removeAllChilds(handle);
    OBJECT_t *obj = findObject(handle);

    if (!obj)
    {
        MSG_WARNING("Object " << handleToString(handle) << " does not exist. Ignoring!");
        draw_mutex.unlock();
        return;
    }

    dropContent(obj);
    removeObject(handle);
    draw_mutex.unlock();
}

void MainWindow::playVideo(ulong handle, ulong parent, int left, int top, int width, int height, const string& url, const string& user, const string& pw)
{
    draw_mutex.lock();
    DECL_TRACER("MainWindow::playVideo(ulong handle, const string& url, const string& user, const string& pw))");

    OBJECT_t *obj = findObject(handle);
    OBJECT_t *par = findObject(parent);
    MSG_DEBUG("Processing button " << handleToString(handle) << " from parent " << handleToString(parent));

    if (!par)
    {
        MSG_WARNING("Button has no parent! Ignoring it.");
        draw_mutex.unlock();
        return;
    }

    if (!obj)
    {
        MSG_DEBUG("Adding new video object ...");
        obj = addObject();

        if (!obj)
        {
            MSG_ERROR("Error creating a video object!");
            TError::setError();
            draw_mutex.unlock();
            return;
        }

        obj->type = OBJ_VIDEO;
        obj->handle = handle;
        obj->width = width;
        obj->height = height;
        obj->left = left;
        obj->top = top;
        obj->object.vwidget = new QVideoWidget(par->object.widget);
        obj->object.vwidget->installEventFilter(this);
    }
    else
        MSG_DEBUG("Object " << handleToString(handle) << " of type " << objectToString(obj->type) << " found!");

    QMediaPlaylist *playlist = new QMediaPlaylist;
    QUrl qurl(url.c_str());

    if (!user.empty())
        qurl.setUserName(user.c_str());

    if (!pw.empty())
        qurl.setPassword(pw.c_str());

    playlist->addMedia(qurl);
    obj->player = new QMediaPlayer;
    obj->player->setPlaylist(playlist);
    obj->player->setVideoOutput(obj->object.vwidget);
    obj->object.vwidget->show();
    obj->player->play();
}

#ifndef QT_NO_SESSIONMANAGER
void MainWindow::commitData(QSessionManager &manager)
{
    if (manager.allowsInteraction())
    {
        if (!settingsChanged)
            manager.cancel();
    }
    else
    {
        if (settingsChanged)
            writeSettings();
    }
}
#endif