Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020 to 2023 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
*/
#include <functional>
#include <map>
#include <QTextObject>
#include <QLabel>
#include <QImage>
#include <QWidget>
#include <QPropertyAnimation>
#include <QListWidget>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QtMultimedia/QMediaPlayer>
#include <QtMultimediaWidgets/QVideoWidget>
#else
#include <QTextLayout>
#endif
#include "tobject.h"
#include "terror.h"
#include "tqscrollarea.h"
#include "tqmarquee.h"
#include "tlock.h"
#include "tqtmain.h"
#include "tresources.h"
using std::string;
using std::map;
using std::pair;
TObject::TObject()
{
DECL_TRACER("TObject::TObject()");
}
TObject::~TObject()
{
DECL_TRACER("TObject::~TObject()");
clear();
}
void TObject::clear(bool force)
{
DECL_TRACER("TObject::clear()");
if (mObjects.empty())
return;
std::map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (!force)
dropContent(&iter->second);
}
mObjects.clear();
}
void TObject::dropContent(OBJECT_t* obj, bool lock)
{
DECL_TRACER("TObject::dropContent(OBJECT_t* obj, bool lock)");
if (!obj)
return;
if (lock)
TLOCKER(mutex_obj);
try
{
switch (obj->type)
{
case OBJ_TEXT:
case OBJ_INPUT:
if (obj->object.plaintext)
{
obj->object.plaintext->close();
obj->object.plaintext = nullptr;
}
obj->wid = 0;
obj->invalid = true;
break;
case OBJ_BUTTON:
if (obj->object.label)
obj->object.label = nullptr;
obj->invalid = true;
break;
case OBJ_MARQUEE:
if (obj->object.marquee)
obj->object.marquee = nullptr;
obj->invalid = true;
break;
// This are the parent widgets (windows) and must be deleted.
// If this widgets are deleted, Qt deletes their children.
case OBJ_PAGE:
case OBJ_SUBPAGE:
obj->invalid = true;
if (obj->object.widget)
{
if (obj->type == OBJ_SUBPAGE)
{
obj->object.widget->close(); // This deletes all childs and the widget itself
obj->object.widget = nullptr;
}
else
obj->object.widget->hide(); // Don't delete a page because it is still stored in the stacked widget.
}
break;
case OBJ_SUBVIEW:
obj->invalid = true;
if (obj->object.area)
{
if (mMainWindow)
{
obj->connected = false;
mMainWindow->disconnectArea(obj->object.area);
}
delete obj->object.area;
obj->object.area = nullptr;
}
break;
case OBJ_VIDEO:
if (obj->object.vwidget)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (obj->player)
delete obj->player;
#endif
obj->object.vwidget = nullptr;
obj->player = nullptr;
}
obj->invalid = true;
break;
case OBJ_LIST:
if (obj->object.list)
{
if (mMainWindow)
mMainWindow->disconnectList(obj->object.list);
obj->object.list->close();
obj->object.list = nullptr;
}
obj->invalid = true;
break;
default:
obj->invalid = true;
break;
}
}
catch (std::exception& e)
{
MSG_ERROR("Error freeing an object: " << e.what());
}
}
void TObject::markDroped(ulong handle)
{
DECL_TRACER("TObject::markDroped(ulong handle)");
OBJECT_t *obj = findObject(handle);
if (!obj)
return;
obj->remove = true;
}
bool TObject::addObject(OBJECT_t& obj)
{
DECL_TRACER("TObject::addObject(OBJECT_t& obj)");
if (obj.handle == 0 || obj.type == OBJ_NONE)
return false;
TLOCKER(mutex_obj);
if (!mObjects.empty() && mObjects.find(obj.handle) != mObjects.end())
return false;
const auto [o, success] = mObjects.insert(pair<ulong, OBJECT_t>(obj.handle, obj));
return success;
}
TObject::OBJECT_t *TObject::findObject(ulong handle)
{
DECL_TRACER("TObject::findObject(ulong handle)");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
if (iter != mObjects.end())
return &iter->second;
return nullptr;
}
TObject::OBJECT_t *TObject::findObject(WId id)
{
DECL_TRACER("TObject::findObject(WId id)");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->second.wid == id)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::findFirstChild(ulong handle)
{
DECL_TRACER("TObject::findFirstChild(ulong handle)");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->first != (handle & 0xffff0000) && (iter->first & 0xffff0000) == (handle & 0xffff0000))
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::findNextChild(ulong handle)
{
DECL_TRACER("TObject::findNextChild(ulong handle)");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
bool next = true;
for (iter = mObjects.find(handle); iter != mObjects.end(); ++iter)
{
if (next)
{
next = false;
continue;
}
if ((iter->first & 0xffff0000) == (handle & 0xffff0000))
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t * TObject::getMarkedRemove()
{
DECL_TRACER("TObject::getMarkedRemove()");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->second.remove)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t * TObject::getNextMarkedRemove(TObject::OBJECT_t* object)
{
DECL_TRACER("TObject::getNextMarkedRemove(TObject::OBJECT_t* obj)");
if (!object || mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
bool next = true;
for (iter = mObjects.find(object->handle); iter != mObjects.end(); ++iter)
{
if (next)
{
next = false;
continue;
}
if (iter->second.remove)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::getFirstDirty()
{
DECL_TRACER("TObject::getFirstDirty()");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->second.dirty)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::getNextDirty(OBJECT_t *obj)
{
DECL_TRACER("TObject::getNextDirty(OBJECT_t *obj)");
if (!obj || mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
bool next = true;
for (iter = mObjects.find(obj->handle); iter != mObjects.end(); ++iter)
{
if (next)
{
next = false;
continue;
}
if (iter->second.dirty)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::findFirstWindow()
{
DECL_TRACER("TObject::getFirstWindow()");
if (mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->second.type == OBJ_SUBPAGE)
return &iter->second;
}
return nullptr;
}
TObject::OBJECT_t *TObject::findNextWindow(TObject::OBJECT_t *obj)
{
DECL_TRACER("TObject::findNextWindow()");
if (!obj || mObjects.empty())
return nullptr;
map<ulong, OBJECT_t>::iterator iter;
bool next = true;
for (iter = mObjects.find(obj->handle); iter != mObjects.end(); ++iter)
{
if (next)
{
next = false;
continue;
}
if (iter->second.type == OBJ_SUBPAGE)
return &iter->second;
}
return nullptr;
}
void TObject::removeObject(ulong handle, bool drop)
{
DECL_TRACER("TObject::removeObject(ulong handle, bool drop)");
if (!handle || mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
if (iter != mObjects.end())
{
if (drop)
dropContent(&iter->second, false);
mObjects.erase(iter);
}
}
void TObject::removeAllChilds(ulong handle, bool drop)
{
DECL_TRACER("TObject::removeAllChilds(ulong handle, bool drop)");
if (!handle || mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter;
bool repeat = false;
do
{
repeat = false;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if ((iter->first & 0xffff0000) == (handle & 0xffff0000) && iter->first != (handle & 0xffff0000))
{
if (drop)
dropContent(&iter->second, false);
mObjects.erase(iter);
repeat = true;
break;
}
}
}
while (repeat);
}
void TObject::cleanMarked()
{
DECL_TRACER("TObject::cleanMarked()");
if (mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter;
bool repeat = false;
do
{
repeat = false;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->second.remove && (!iter->second.animation || iter->second.animation->state() != QAbstractAnimation::Running))
{
if (iter->second.type == OBJ_SUBPAGE && iter->second.object.widget)
{
iter->second.object.widget->close();
iter->second.object.widget = nullptr;
}
mObjects.erase(iter);
repeat = true;
break;
}
}
}
while (repeat);
}
void TObject::invalidateAllObjects()
{
DECL_TRACER("TObject::invalidateAllObjects()");
if (mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
iter->second.remove = false;
iter->second.invalid = true;
}
}
void TObject::invalidateObject(ulong handle)
{
DECL_TRACER("TObject::invalidateObject(ulong handle)");
if (mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
if (iter != mObjects.end())
{
iter->second.remove = false;
iter->second.invalid = true;
}
}
void TObject::invalidateAllSubObjects(ulong handle)
{
DECL_TRACER("TObject::invalidateAllSubObjects(ulong handle)");
if (mObjects.empty())
return;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter;
bool first = true;
for (iter = mObjects.find(handle); iter != mObjects.end(); ++iter)
{
if (first)
{
first = false;
continue;
}
if ((iter->first & 0xffff0000) == handle)
{
MSG_DEBUG("Invalidating object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type));
iter->second.remove = false;
iter->second.invalid = true;
if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && iter->second.connected)
{
iter->second.connected = false;
mMainWindow->disconnectArea(iter->second.object.area);
}
else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && iter->second.connected)
{
iter->second.connected = false;
mMainWindow->disconnectList(iter->second.object.list);
}
}
}
}
bool TObject::enableObject(ulong handle)
{
DECL_TRACER("TObject::enableObject(ulong handle)");
if (mObjects.empty())
return false;
map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
if (iter != mObjects.end())
{
if (!iter->second.object.widget)
{
iter->second.remove = true;
MSG_ERROR("Object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type) << " has no QObject!");
return false;
}
TLOCKER(mutex_obj);
iter->second.remove = false;
iter->second.invalid = false;
if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && !iter->second.connected)
{
mMainWindow->reconnectArea(iter->second.object.area);
iter->second.connected = true;
}
else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && !iter->second.connected)
{
mMainWindow->reconnectList(iter->second.object.list);
iter->second.connected = true;
}
return true; // We can savely return here because a handle is unique
}
return false;
}
bool TObject::enableAllSubObjects(ulong handle)
{
DECL_TRACER("::enableAllSubObjects(ulong handle)");
if (mObjects.empty())
return false;
TLOCKER(mutex_obj);
map<ulong, OBJECT_t>::iterator iter;
bool ret = true;
for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
{
if (iter->first != handle && (iter->first & 0xffff0000) == handle)
{
if (!iter->second.object.widget)
{
iter->second.remove = true;
MSG_ERROR("Object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type) << " has no QObject!");
ret = false;
}
else
{
iter->second.remove = false;
iter->second.invalid = false;
if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && !iter->second.connected)
{
mMainWindow->reconnectArea(iter->second.object.area);
iter->second.connected = true;
}
else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && !iter->second.connected)
{
mMainWindow->reconnectList(iter->second.object.list);
iter->second.connected = true;
}
}
}
}
return ret;
}
string TObject::objectToString(TObject::OBJECT_TYPE o)
{
switch(o)
{
case OBJ_BUTTON: return "BUTTON"; break;
case OBJ_MARQUEE: return "MARQUEE"; break;
case OBJ_INPUT: return "INPUT"; break;
case OBJ_NONE: return "undefined"; break;
case OBJ_PAGE: return "PAGE"; break;
case OBJ_SUBPAGE: return "SUBPAGE"; break;
case OBJ_TEXT: return "TEXT"; break;
case OBJ_VIDEO: return "VIDEO"; break;
case OBJ_LIST: return "LIST"; break;
case OBJ_SUBVIEW: return "SUBVIEW"; break;
}
return string(); // Should not happen but is needed to satisfy the compiler.
}