Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
2
 * Copyright (C) 2020 to 2023 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <functional>
20
#include <map>
21
 
22
#include <QTextObject>
23
#include <QLabel>
24
#include <QImage>
25
#include <QWidget>
26
#include <QPropertyAnimation>
27
#include <QListWidget>
28
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
29
#include <QtMultimedia/QMediaPlayer>
30
#include <QtMultimediaWidgets/QVideoWidget>
31
#else
32
#include <QTextLayout>
33
#endif
34
#include "tobject.h"
35
#include "terror.h"
36
#include "tqscrollarea.h"
37
#include "tqmarquee.h"
38
#include "tlock.h"
39
#include "tqtmain.h"
40
#include "tresources.h"
41
 
42
using std::string;
43
using std::map;
44
using std::pair;
45
 
46
TObject::TObject()
47
{
48
    DECL_TRACER("TObject::TObject()");
49
}
50
 
51
TObject::~TObject()
52
{
53
    DECL_TRACER("TObject::~TObject()");
54
 
55
    clear();
56
}
57
 
58
void TObject::clear(bool force)
59
{
60
    DECL_TRACER("TObject::clear()");
61
 
62
    if (mObjects.empty())
63
        return;
64
 
65
    std::map<ulong, OBJECT_t>::iterator iter;
66
 
67
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
68
    {
69
        if (!force)
70
            dropContent(&iter->second);
71
    }
72
 
73
    mObjects.clear();
74
}
75
 
76
void TObject::dropContent(OBJECT_t* obj, bool lock)
77
{
78
    DECL_TRACER("TObject::dropContent(OBJECT_t* obj, bool lock)");
79
 
80
    if (!obj)
81
        return;
82
 
83
    if (lock)
84
        TLOCKER(mutex_obj);
85
 
86
    try
87
    {
88
        switch (obj->type)
89
        {
90
            case OBJ_TEXT:
91
            case OBJ_INPUT:
92
                if (obj->object.plaintext)
93
                {
94
                    obj->object.plaintext->close();
95
                    obj->object.plaintext = nullptr;
96
                }
97
 
98
                obj->wid = 0;
99
                obj->invalid = true;
100
            break;
101
 
102
            case OBJ_BUTTON:
103
                if (obj->object.label)
104
                    obj->object.label = nullptr;
105
 
106
                obj->invalid = true;
107
            break;
108
 
109
            case OBJ_MARQUEE:
110
                if (obj->object.marquee)
111
                    obj->object.marquee = nullptr;
112
 
113
                obj->invalid = true;
114
            break;
115
 
116
            // This are the parent widgets (windows) and must be deleted.
117
            // If this widgets are deleted, Qt deletes their children.
118
            case OBJ_PAGE:
119
            case OBJ_SUBPAGE:
120
                obj->invalid = true;
121
 
122
                if (obj->object.widget)
123
                {
124
                    if (obj->type == OBJ_SUBPAGE)
125
                    {
126
                        obj->object.widget->close();        // This deletes all childs and the widget itself
127
                        obj->object.widget = nullptr;
128
                    }
129
                    else
130
                        obj->object.widget->hide();         // Don't delete a page because it is still stored in the stacked widget.
131
                }
132
            break;
133
 
134
            case OBJ_SUBVIEW:
135
                obj->invalid = true;
136
 
137
                if (obj->object.area)
138
                {
139
                    if (mMainWindow)
140
                    {
141
                        obj->connected = false;
142
                        mMainWindow->disconnectArea(obj->object.area);
143
                    }
144
 
145
                    delete obj->object.area;
146
                    obj->object.area = nullptr;
147
                }
148
            break;
149
 
150
            case OBJ_VIDEO:
151
                if (obj->object.vwidget)
152
                {
153
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
154
                    if (obj->player)
155
                        delete obj->player;
156
#endif
157
                    obj->object.vwidget = nullptr;
158
                    obj->player = nullptr;
159
                }
160
 
161
                obj->invalid = true;
162
            break;
163
 
164
            case OBJ_LIST:
165
                if (obj->object.list)
166
                {
167
                    if (mMainWindow)
168
                        mMainWindow->disconnectList(obj->object.list);
169
 
170
                    obj->object.list->close();
171
                    obj->object.list = nullptr;
172
                }
173
 
174
                obj->invalid = true;
175
            break;
176
 
177
            default:
178
                obj->invalid = true;
179
                break;
180
        }
181
    }
182
    catch (std::exception& e)
183
    {
184
        MSG_ERROR("Error freeing an object: " << e.what());
185
    }
186
}
187
 
188
void TObject::markDroped(ulong handle)
189
{
190
    DECL_TRACER("TObject::markDroped(ulong handle)");
191
 
192
    OBJECT_t *obj = findObject(handle);
193
 
194
    if (!obj)
195
        return;
196
 
197
    obj->remove = true;
198
}
199
 
200
bool TObject::addObject(OBJECT_t& obj)
201
{
202
    DECL_TRACER("TObject::addObject(OBJECT_t& obj)");
203
 
204
    if (obj.handle == 0 || obj.type == OBJ_NONE)
205
        return false;
206
 
207
    TLOCKER(mutex_obj);
208
 
209
    if (!mObjects.empty() && mObjects.find(obj.handle) != mObjects.end())
210
        return false;
211
 
212
    const auto [o, success] = mObjects.insert(pair<ulong, OBJECT_t>(obj.handle, obj));
213
    return success;
214
}
215
 
216
TObject::OBJECT_t *TObject::findObject(ulong handle)
217
{
218
    DECL_TRACER("TObject::findObject(ulong handle)");
219
 
220
    if (mObjects.empty())
221
        return nullptr;
222
 
223
    map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
224
 
225
    if (iter != mObjects.end())
226
        return &iter->second;
227
 
228
    return nullptr;
229
}
230
 
231
TObject::OBJECT_t *TObject::findObject(WId id)
232
{
233
    DECL_TRACER("TObject::findObject(WId id)");
234
 
235
    if (mObjects.empty())
236
        return nullptr;
237
 
238
    map<ulong, OBJECT_t>::iterator iter;
239
 
240
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
241
    {
242
        if (iter->second.wid == id)
243
            return &iter->second;
244
    }
245
 
246
    return nullptr;
247
}
248
 
249
TObject::OBJECT_t *TObject::findFirstChild(ulong handle)
250
{
251
    DECL_TRACER("TObject::findFirstChild(ulong handle)");
252
 
253
    if (mObjects.empty())
254
        return nullptr;
255
 
256
    map<ulong, OBJECT_t>::iterator iter;
257
 
258
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
259
    {
260
        if (iter->first != (handle & 0xffff0000) && (iter->first & 0xffff0000) == (handle & 0xffff0000))
261
            return &iter->second;
262
    }
263
 
264
    return nullptr;
265
}
266
 
267
TObject::OBJECT_t *TObject::findNextChild(ulong handle)
268
{
269
    DECL_TRACER("TObject::findNextChild(ulong handle)");
270
 
271
    if (mObjects.empty())
272
        return nullptr;
273
 
274
    map<ulong, OBJECT_t>::iterator iter;
275
    bool next = true;
276
 
277
    for (iter = mObjects.find(handle); iter != mObjects.end(); ++iter)
278
    {
279
        if (next)
280
        {
281
            next = false;
282
            continue;
283
        }
284
 
285
        if ((iter->first & 0xffff0000) == (handle & 0xffff0000))
286
            return &iter->second;
287
    }
288
 
289
    return nullptr;
290
}
291
 
292
TObject::OBJECT_t * TObject::getMarkedRemove()
293
{
294
    DECL_TRACER("TObject::getMarkedRemove()");
295
 
296
    if (mObjects.empty())
297
        return nullptr;
298
 
299
    map<ulong, OBJECT_t>::iterator iter;
300
 
301
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
302
    {
303
        if (iter->second.remove)
304
            return &iter->second;
305
    }
306
 
307
    return nullptr;
308
}
309
 
310
TObject::OBJECT_t * TObject::getNextMarkedRemove(TObject::OBJECT_t* object)
311
{
312
    DECL_TRACER("TObject::getNextMarkedRemove(TObject::OBJECT_t* obj)");
313
 
314
    if (!object || mObjects.empty())
315
        return nullptr;
316
 
317
    map<ulong, OBJECT_t>::iterator iter;
318
    bool next = true;
319
 
320
    for (iter = mObjects.find(object->handle); iter != mObjects.end(); ++iter)
321
    {
322
        if (next)
323
        {
324
            next = false;
325
            continue;
326
        }
327
 
328
        if (iter->second.remove)
329
            return &iter->second;
330
    }
331
 
332
    return nullptr;
333
}
334
 
335
TObject::OBJECT_t *TObject::getFirstDirty()
336
{
337
    DECL_TRACER("TObject::getFirstDirty()");
338
 
339
    if (mObjects.empty())
340
        return nullptr;
341
 
342
    map<ulong, OBJECT_t>::iterator iter;
343
 
344
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
345
    {
346
        if (iter->second.dirty)
347
            return &iter->second;
348
    }
349
 
350
    return nullptr;
351
}
352
 
353
TObject::OBJECT_t *TObject::getNextDirty(OBJECT_t *obj)
354
{
355
    DECL_TRACER("TObject::getNextDirty(OBJECT_t *obj)");
356
 
357
    if (!obj || mObjects.empty())
358
        return nullptr;
359
 
360
    map<ulong, OBJECT_t>::iterator iter;
361
    bool next = true;
362
 
363
    for (iter = mObjects.find(obj->handle); iter != mObjects.end(); ++iter)
364
    {
365
        if (next)
366
        {
367
            next = false;
368
            continue;
369
        }
370
 
371
        if (iter->second.dirty)
372
            return &iter->second;
373
    }
374
 
375
    return nullptr;
376
}
377
TObject::OBJECT_t *TObject::findFirstWindow()
378
{
379
    DECL_TRACER("TObject::getFirstWindow()");
380
 
381
    if (mObjects.empty())
382
        return nullptr;
383
 
384
    map<ulong, OBJECT_t>::iterator iter;
385
 
386
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
387
    {
388
        if (iter->second.type == OBJ_SUBPAGE)
389
            return &iter->second;
390
    }
391
 
392
    return nullptr;
393
}
394
 
395
TObject::OBJECT_t *TObject::findNextWindow(TObject::OBJECT_t *obj)
396
{
397
    DECL_TRACER("TObject::findNextWindow()");
398
 
399
    if (!obj || mObjects.empty())
400
        return nullptr;
401
 
402
    map<ulong, OBJECT_t>::iterator iter;
403
    bool next = true;
404
 
405
    for (iter = mObjects.find(obj->handle); iter != mObjects.end(); ++iter)
406
    {
407
        if (next)
408
        {
409
            next = false;
410
            continue;
411
        }
412
 
413
        if (iter->second.type == OBJ_SUBPAGE)
414
            return &iter->second;
415
    }
416
 
417
    return nullptr;
418
}
419
 
420
void TObject::removeObject(ulong handle, bool drop)
421
{
422
    DECL_TRACER("TObject::removeObject(ulong handle, bool drop)");
423
 
424
    if (!handle || mObjects.empty())
425
        return;
426
 
427
    TLOCKER(mutex_obj);
428
    map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
429
 
430
    if (iter != mObjects.end())
431
    {
432
        if (drop)
433
            dropContent(&iter->second, false);
434
 
435
        mObjects.erase(iter);
436
    }
437
}
438
 
439
void TObject::removeAllChilds(ulong handle, bool drop)
440
{
441
    DECL_TRACER("TObject::removeAllChilds(ulong handle, bool drop)");
442
 
443
    if (!handle || mObjects.empty())
444
        return;
445
 
446
    TLOCKER(mutex_obj);
447
    map<ulong, OBJECT_t>::iterator iter;
448
    bool repeat = false;
449
 
450
    do
451
    {
452
        repeat = false;
453
 
454
        for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
455
        {
456
            if ((iter->first & 0xffff0000) == (handle & 0xffff0000) && iter->first != (handle & 0xffff0000))
457
            {
458
                if (drop)
459
                    dropContent(&iter->second, false);
460
 
461
                mObjects.erase(iter);
462
                repeat = true;
463
                break;
464
            }
465
        }
466
    }
467
    while (repeat);
468
}
469
 
470
void TObject::cleanMarked()
471
{
472
    DECL_TRACER("TObject::cleanMarked()");
473
 
474
    if (mObjects.empty())
475
        return;
476
 
477
    TLOCKER(mutex_obj);
478
    map<ulong, OBJECT_t>::iterator iter;
479
    bool repeat = false;
480
 
481
    do
482
    {
483
        repeat = false;
484
 
485
        for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
486
        {
487
            if (iter->second.remove && (!iter->second.animation || iter->second.animation->state() != QAbstractAnimation::Running))
488
            {
489
                if (iter->second.type == OBJ_SUBPAGE && iter->second.object.widget)
490
                {
491
                    iter->second.object.widget->close();
492
                    iter->second.object.widget = nullptr;
493
                }
494
 
495
                mObjects.erase(iter);
496
                repeat = true;
497
                break;
498
            }
499
        }
500
    }
501
    while (repeat);
502
}
503
 
504
void TObject::invalidateAllObjects()
505
{
506
    DECL_TRACER("TObject::invalidateAllObjects()");
507
 
508
    if (mObjects.empty())
509
        return;
510
 
511
    TLOCKER(mutex_obj);
512
    map<ulong, OBJECT_t>::iterator iter;
513
 
514
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
515
    {
516
        iter->second.remove = false;
517
        iter->second.invalid = true;
518
    }
519
}
520
 
521
void TObject::invalidateObject(ulong handle)
522
{
523
    DECL_TRACER("TObject::invalidateObject(ulong handle)");
524
 
525
    if (mObjects.empty())
526
        return;
527
 
528
    TLOCKER(mutex_obj);
529
    map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
530
 
531
    if (iter != mObjects.end())
532
    {
533
        iter->second.remove = false;
534
        iter->second.invalid = true;
535
    }
536
}
537
 
538
void TObject::invalidateAllSubObjects(ulong handle)
539
{
540
    DECL_TRACER("TObject::invalidateAllSubObjects(ulong handle)");
541
 
542
    if (mObjects.empty())
543
        return;
544
 
545
    TLOCKER(mutex_obj);
546
 
547
    map<ulong, OBJECT_t>::iterator iter;
548
    bool first = true;
549
 
550
    for (iter = mObjects.find(handle); iter != mObjects.end(); ++iter)
551
    {
552
        if (first)
553
        {
554
            first = false;
555
            continue;
556
        }
557
 
558
        if ((iter->first & 0xffff0000) == handle)
559
        {
560
            MSG_DEBUG("Invalidating object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type));
561
            iter->second.remove = false;
562
            iter->second.invalid = true;
563
 
564
            if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && iter->second.connected)
565
            {
566
                iter->second.connected = false;
567
                mMainWindow->disconnectArea(iter->second.object.area);
568
            }
569
            else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && iter->second.connected)
570
            {
571
                iter->second.connected = false;
572
                mMainWindow->disconnectList(iter->second.object.list);
573
            }
574
        }
575
    }
576
}
577
 
578
bool TObject::enableObject(ulong handle)
579
{
580
    DECL_TRACER("TObject::enableObject(ulong handle)");
581
 
582
    if (mObjects.empty())
583
        return false;
584
 
585
    map<ulong, OBJECT_t>::iterator iter = mObjects.find(handle);
586
 
587
    if (iter != mObjects.end())
588
    {
589
        if (!iter->second.object.widget)
590
        {
591
            iter->second.remove = true;
592
            MSG_ERROR("Object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type) << " has no QObject!");
593
            return false;
594
        }
595
 
596
        TLOCKER(mutex_obj);
597
        iter->second.remove = false;
598
        iter->second.invalid = false;
599
 
600
        if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && !iter->second.connected)
601
        {
602
            mMainWindow->reconnectArea(iter->second.object.area);
603
            iter->second.connected = true;
604
        }
605
        else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && !iter->second.connected)
606
        {
607
            mMainWindow->reconnectList(iter->second.object.list);
608
            iter->second.connected = true;
609
        }
610
 
611
        return true;                         // We can savely return here because a handle is unique
612
    }
613
 
614
    return false;
615
}
616
 
617
bool TObject::enableAllSubObjects(ulong handle)
618
{
619
    DECL_TRACER("::enableAllSubObjects(ulong handle)");
620
 
621
    if (mObjects.empty())
622
        return false;
623
 
624
    TLOCKER(mutex_obj);
625
    map<ulong, OBJECT_t>::iterator iter;
626
    bool ret = true;
627
 
628
    for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
629
    {
630
        if (iter->first != handle && (iter->first & 0xffff0000) == handle)
631
        {
632
            if (!iter->second.object.widget)
633
            {
634
                iter->second.remove = true;
635
                MSG_ERROR("Object " << handleToString(iter->first) << " of type " << objectToString(iter->second.type) << " has no QObject!");
636
                ret = false;
637
            }
638
            else
639
            {
640
                iter->second.remove = false;
641
                iter->second.invalid = false;
642
 
643
                if (iter->second.type == OBJ_SUBVIEW && iter->second.object.area && mMainWindow && !iter->second.connected)
644
                {
645
                    mMainWindow->reconnectArea(iter->second.object.area);
646
                    iter->second.connected = true;
647
                }
648
                else if (iter->second.type == OBJ_LIST && iter->second.object.list && mMainWindow && !iter->second.connected)
649
                {
650
                    mMainWindow->reconnectList(iter->second.object.list);
651
                    iter->second.connected = true;
652
                }
653
            }
654
        }
655
    }
656
 
657
    return ret;
658
}
659
 
660
string TObject::objectToString(TObject::OBJECT_TYPE o)
661
{
662
    switch(o)
663
    {
664
        case OBJ_BUTTON:  return "BUTTON"; break;
665
        case OBJ_MARQUEE: return "MARQUEE"; break;
666
        case OBJ_INPUT:   return "INPUT"; break;
667
        case OBJ_NONE:    return "undefined"; break;
668
        case OBJ_PAGE:    return "PAGE"; break;
669
        case OBJ_SUBPAGE: return "SUBPAGE"; break;
670
        case OBJ_TEXT:    return "TEXT"; break;
671
        case OBJ_VIDEO:   return "VIDEO"; break;
672
        case OBJ_LIST:    return "LIST"; break;
673
        case OBJ_SUBVIEW: return "SUBVIEW"; break;
674
    }
675
 
676
    return string();   // Should not happen but is needed to satisfy the compiler.
677
}