Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 andreas 1
/*
21 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
3 andreas 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
 
5 andreas 19
#include <exception>
20
 
6 andreas 21
#include "tresources.h"
3 andreas 22
#include "treadxml.h"
26 andreas 23
#include "tpagemanager.h"
3 andreas 24
#include "tsubpage.h"
25
#include "tconfig.h"
26
#include "terror.h"
27
 
28
using std::string;
6 andreas 29
using std::vector;
3 andreas 30
using namespace Button;
31
 
26 andreas 32
extern TPageManager *gPageManager;
33
 
3 andreas 34
TSubPage::TSubPage(const string& name)
35
    : mFile(name)
36
{
37
    DECL_TRACER("TSubPage::TSubPage(const string& path)");
14 andreas 38
    TError::clear();
3 andreas 39
    string path = makeFileName(TConfig::getProjectPath(), name);
40
 
41
    if (isValidFile())
42
        mFName = getFileName();
43
    else
44
    {
45
        MSG_ERROR("Either the path \"" << TConfig::getProjectPath() << "\" or the file name \"" << name << "\" is invalid!");
46
        TError::setError();
47
        return;
48
    }
49
 
50
    initialize();
51
}
52
 
53
TSubPage::~TSubPage()
54
{
55
    DECL_TRACER("TSubPage::~TSubPage()");
56
 
5 andreas 57
    if (mSubpage.name.empty())
58
    {
59
        MSG_WARNING("Invalid page found!");
60
        return;
61
    }
62
 
63
    MSG_DEBUG("Destroing subpage " << mSubpage.pageID << ": " << mSubpage.name);
64
 
3 andreas 65
    BUTTONS_T *b = mButtons;
66
    BUTTONS_T *next = nullptr;
67
 
68
    while (b)
69
    {
70
        next = b->next;
5 andreas 71
 
72
        if (b->button)
73
            delete b->button;
74
 
3 andreas 75
        delete b;
76
        b = next;
77
    }
78
 
79
    mButtons = nullptr;
80
}
81
 
82
void TSubPage::initialize()
83
{
84
    DECL_TRACER("TSubPage::initialize()");
85
 
86
    if (mFName.empty())
87
        return;
88
 
89
    TError::clear();
90
    TReadXML reader(mFName);
91
 
92
    if (TError::isError())
93
    {
94
        MSG_DEBUG("Stopped scanning the subpage " << mFile << " due to previous errors!");
95
        return;
96
    }
97
 
98
    reader.findElement("page", "type");
99
 
100
    if (!reader.success())
101
    {
102
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found! Invalid XML file!");
103
        TError::setError();
104
        return;
105
    }
106
 
107
    if (reader.getAttribute("type").compare("subpage") != 0)
108
    {
109
        MSG_ERROR("The type " << reader.getAttribute("type") << " is invalid for a subpage!");
110
        TError::setError();
111
        return;
112
    }
113
 
114
    mSubpage.popupType = reader.getAttribute("popupType");
115
    mxml_node_t *node = reader.getFirstChild();
116
 
117
    while (node)
118
    {
119
        string ename = reader.getElementName(node);
120
 
121
        if (ename.compare("pageID") == 0)
122
            mSubpage.pageID = reader.getIntFromNode(node);
123
        else if (ename.compare("name") == 0)
124
            mSubpage.name = reader.getTextFromNode(node);
7 andreas 125
        else if (ename.compare("left") == 0)
126
            mSubpage.left = reader.getIntFromNode(node);
127
        else if (ename.compare("top") == 0)
128
            mSubpage.top = reader.getIntFromNode(node);
3 andreas 129
        else if (ename.compare("width") == 0)
130
            mSubpage.width = reader.getIntFromNode(node);
131
        else if (ename.compare("height") == 0)
132
            mSubpage.height = reader.getIntFromNode(node);
133
        else if (ename.compare("group") == 0)
134
            mSubpage.group = reader.getTextFromNode(node);
7 andreas 135
        else if (ename.compare("showEffect") == 0)
136
            mSubpage.showEffect = (SHOWEFFECT)reader.getIntFromNode(node);
3 andreas 137
        else if (ename.compare("showTime") == 0)
138
            mSubpage.showTime = reader.getIntFromNode(node);
139
        else if (ename.compare("hideTime") == 0)
140
            mSubpage.hideTime = reader.getIntFromNode(node);
7 andreas 141
        else if (ename.compare("hideEffect") == 0)
142
            mSubpage.hideEffect = (SHOWEFFECT)reader.getIntFromNode(node);
3 andreas 143
        else if (ename.compare("timeout") == 0)
144
            mSubpage.timeout = reader.getIntFromNode(node);
145
        else if (ename.compare("button") == 0)      // Read a button
146
        {
147
            try
148
            {
149
                TButton *button = new TButton();
4 andreas 150
                button->setPalette(mPalette);
7 andreas 151
                button->setFonts(mFonts);
3 andreas 152
                button->initialize(&reader, node);
6 andreas 153
                button->setParentSize(mSubpage.width, mSubpage.height);
7 andreas 154
                button->registerCallback(_displayButton);
155
                button->registerCallbackFT(_setText);
21 andreas 156
                button->regCallPlayVideo(_playVideo);
3 andreas 157
 
158
                if (TError::isError())
159
                {
16 andreas 160
                    MSG_ERROR("Dropping button because of previous errors!");
3 andreas 161
                    delete button;
162
                    return;
163
                }
164
 
5 andreas 165
                button->setHandle(((mSubpage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
14 andreas 166
                button->createButtons();
3 andreas 167
                addButton(button);
168
            }
169
            catch (std::exception& e)
170
            {
171
                MSG_ERROR("Memory exception: " << e.what());
172
                TError::setError();
173
                return;
174
            }
175
        }
6 andreas 176
        else if (ename.compare("sr") == 0)
177
        {
178
            SR_T sr;
179
            sr.number = atoi(reader.getAttributeFromNode(node, "number").c_str());
180
            mxml_node_t *n = reader.getFirstChild(node);
3 andreas 181
 
6 andreas 182
            while (n)
183
            {
184
                string ename = reader.getElementName(n);
185
 
186
                if (ename.compare("bs") == 0)
187
                    sr.bs = reader.getTextFromNode(n);
188
                else if (ename.compare("cb") == 0)
189
                    sr.cb = reader.getTextFromNode(n);
190
                else if (ename.compare("cf") == 0)
191
                    sr.cf = reader.getTextFromNode(n);
192
                else if (ename.compare("ct") == 0)
193
                    sr.ct = reader.getTextFromNode(n);
194
                else if (ename.compare("ec") == 0)
195
                    sr.ec = reader.getTextFromNode(n);
196
                else if (ename.compare("bm") == 0)
197
                    sr.bm = reader.getTextFromNode(n);
10 andreas 198
                else if (ename.compare("ji") == 0)
199
                    sr.ji = reader.getIntFromNode(n);
200
                else if (ename.compare("jb") == 0)
201
                    sr.jb = reader.getIntFromNode(n);
6 andreas 202
                else if (ename.compare("fi") == 0)
203
                    sr.fi = reader.getIntFromNode(n);
10 andreas 204
                else if (ename.compare("ii") == 0)
205
                    sr.ii = reader.getIntFromNode(n);
206
                else if (ename.compare("ix") == 0)
207
                    sr.ix = reader.getIntFromNode(n);
208
                else if (ename.compare("iy") == 0)
209
                    sr.iy = reader.getIntFromNode(n);
210
                else if (ename.compare("oo") == 0)
211
                    sr.oo = reader.getIntFromNode(n);
6 andreas 212
 
213
                n = reader.getNextChild(n);
214
            }
215
 
216
            mSubpage.sr.push_back(sr);
217
        }
218
 
3 andreas 219
        node = reader.getNextChild();
220
    }
8 andreas 221
 
222
    // Here the sort function could be called. But it's not necessary because
223
    // the buttons are stored in ascending Z order. Therefor the following
224
    // method call is commented out.
225
    // sortButtons();
6 andreas 226
}
3 andreas 227
 
6 andreas 228
void TSubPage::show()
229
{
230
    DECL_TRACER("TSubPage::show()");
3 andreas 231
 
6 andreas 232
    if (!_setBackground)
3 andreas 233
    {
6 andreas 234
        MSG_WARNING("No callback \"setBackground\" was set!");
3 andreas 235
        return;
236
    }
237
 
6 andreas 238
    ulong handle = (mSubpage.pageID << 16) & 0xffff0000;
239
    MSG_DEBUG("Processing page " << mSubpage.pageID << ": " << mSubpage.name);
240
    // Draw the background, if any
241
    if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].bm.empty())
3 andreas 242
    {
6 andreas 243
        MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
244
        sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
3 andreas 245
 
6 andreas 246
        if (rawImage)
3 andreas 247
        {
6 andreas 248
            SkBitmap bm;
249
            MSG_DEBUG("Decoding image ...");
19 andreas 250
 
251
            if (!DecodeDataToBitmap(rawImage, &bm))
252
                MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
253
 
6 andreas 254
            SkImageInfo info = bm.info();
255
            size_t rowBytes = info.minRowBytes();
256
            size_t size = info.computeByteSize(rowBytes);
31 andreas 257
            int left, top;
258
            calcPosition(info.width(), info.height(), &left, &top);
259
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(bm);
260
            bm.allocN32Pixels(mSubpage.width, mSubpage.height);
261
            bm.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
262
            SkCanvas can(bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
263
            SkRect rect = SkRect::MakeXYWH(left, top, info.width(), info.height());
264
            SkPaint paint;
265
            paint.setBlendMode(SkBlendMode::kSrc);
266
            can.drawImageRect(im, rect, &paint);
267
            rowBytes = bm.info().minRowBytes();
268
            size = bm.info().computeByteSize(rowBytes);
269
            MSG_DEBUG("Normal size of background image: " << left << ", " << top << ", " << info.width() << ", " << info.height());
3 andreas 270
 
26 andreas 271
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
272
            {
273
                SkPaint paint;
274
                paint.setBlendMode(SkBlendMode::kSrc);
275
                paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 276
                // Calculate new dimension
31 andreas 277
                double scaleFactor = gPageManager->getScaleFactor();
278
                MSG_DEBUG("Using scale factor " << scaleFactor);
279
                int lwidth = (int)((double)info.width() * scaleFactor);
280
                int lheight = (int)((double)info.height() * scaleFactor);
281
                int twidth = (int)((double)mSubpage.width * scaleFactor);
282
                int theight = (int)((double)mSubpage.height * scaleFactor);
283
                calcPosition(lwidth, lheight, &left, &top, true);
28 andreas 284
                // Create a canvas and draw new image
26 andreas 285
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(bm);
31 andreas 286
                bm.allocN32Pixels(twidth, theight);
287
                bm.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
26 andreas 288
                SkCanvas can(bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
31 andreas 289
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
26 andreas 290
                can.drawImageRect(im, rect, &paint);
28 andreas 291
                rowBytes = bm.info().minRowBytes();
292
                size = bm.info().computeByteSize(rowBytes);
31 andreas 293
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
26 andreas 294
            }
295
 
19 andreas 296
            MSG_DEBUG("Setting background with image of size " << size);
297
            _setBackground(handle, (unsigned char *)bm.getPixels(), size, rowBytes, TColor::getColor(mSubpage.sr[0].cf));
6 andreas 298
        }
299
        else
300
        {
301
            MSG_WARNING("Couldn't read image " << mSubpage.sr[0].bm);
3 andreas 302
 
19 andreas 303
            if (mSubpage.sr.size() > 0)
6 andreas 304
                _setBackground(handle, nullptr, 0, 0, TColor::getColor(mSubpage.sr[0].cf));
3 andreas 305
        }
6 andreas 306
    }
19 andreas 307
    else if (mSubpage.sr.size() > 0)
6 andreas 308
    {
309
        MSG_DEBUG("Calling \"setBackground\" with no image ...");
310
        _setBackground(handle, nullptr, 0, 0, TColor::getColor(mSubpage.sr[0].cf));
311
    }
3 andreas 312
 
6 andreas 313
    // Draw the buttons
314
    BUTTONS_T *button = mButtons;
315
 
316
    while (button)
317
    {
318
        if (button->button)
319
        {
320
            MSG_DEBUG("Drawing button " << button->button->getButtonIndex() << ": " << button->button->getButtonName());
321
            button->button->registerCallback(_displayButton);
7 andreas 322
            button->button->registerCallbackFT(_setText);
21 andreas 323
            button->button->regCallPlayVideo(_playVideo);
7 andreas 324
            button->button->setFonts(mFonts);
325
            button->button->setPalette(mPalette);
6 andreas 326
            button->button->createButtons();
10 andreas 327
 
328
            if (mSubpage.sr.size() > 0)
329
                button->button->setGlobalOpacity(mSubpage.sr[0].oo);
330
 
6 andreas 331
            button->button->show();
332
        }
333
 
334
        button = button->next;
3 andreas 335
    }
11 andreas 336
 
337
    // Mark page as visible
338
    mVisible = true;
3 andreas 339
}
340
 
11 andreas 341
void TSubPage::drop()
342
{
343
    DECL_TRACER("TSubPage::drop()");
344
 
14 andreas 345
    if (mVisible && _callDropSubPage)
11 andreas 346
        _callDropSubPage((mSubpage.pageID << 16) & 0xffff0000);
347
 
15 andreas 348
    // Set all elements of subpage invisible
349
    BUTTONS_T *bt = mButtons;
350
 
351
    while (bt)
352
    {
353
        bt->button->hide();
354
        bt = bt->next;
355
    }
356
 
14 andreas 357
    mZOrder = -1;
11 andreas 358
    mVisible = false;
359
}
360
 
3 andreas 361
BUTTONS_T *TSubPage::addButton(TButton* button)
362
{
363
    DECL_TRACER("*TSubPage::addButton(TButton* button)");
364
 
365
    if (!button)
366
    {
367
        MSG_ERROR("Parameter is NULL!");
368
        TError::setError();
369
        return nullptr;
370
    }
371
 
372
    try
373
    {
374
        BUTTONS_T *chain = new BUTTONS_T;
375
        chain->button = button;
14 andreas 376
        chain->next = nullptr;
377
        chain->previous = nullptr;
378
        BUTTONS_T *bts = mButtons;
3 andreas 379
 
14 andreas 380
        if (bts)
3 andreas 381
        {
14 andreas 382
            BUTTONS_T *p = bts;
3 andreas 383
 
384
            while (p->next)
385
                p = p->next;
386
 
387
            p->next = chain;
388
            chain->previous = p;
389
        }
390
        else
391
            mButtons = chain;
392
 
393
        return chain;
394
    }
395
    catch (std::exception& e)
396
    {
397
        MSG_ERROR("Memory error: " << e.what());
398
        TError::setError();
399
    }
400
 
401
    return nullptr;
402
}
403
 
14 andreas 404
bool TSubPage::hasButton(int id)
405
{
406
    DECL_TRACER("TSubPage::hasButton(int id)");
407
 
408
    BUTTONS_T *bt = mButtons;
409
 
410
    while (bt)
411
    {
412
        if (bt->button && bt->button->getButtonIndex() == id)
413
            return true;
414
 
415
        bt = bt->next;
416
    }
417
 
418
    return false;
419
}
420
 
421
TButton *TSubPage::getButton(int id)
422
{
423
    DECL_TRACER("TSubPage::getButton(int id)");
424
 
425
    BUTTONS_T *bt = mButtons;
426
 
427
    while (bt)
428
    {
429
        if (bt->button && bt->button->getButtonIndex() == id)
430
            return bt->button;
431
 
432
        bt = bt->next;
433
    }
434
 
435
    return nullptr;
436
}
437
 
16 andreas 438
vector<TButton *> TSubPage::getButtons(int ap, int ad)
439
{
440
    DECL_TRACER("TSubPage::getButtons(int ap, int ad)");
441
 
442
    vector<TButton *> list;
443
    BUTTONS_T *bt = mButtons;
444
 
445
    while (bt)
446
    {
447
        if (bt->button->getAddressPort() == ap && bt->button->getAddressChannel() == ad)
448
            list.push_back(bt->button);
449
 
450
        bt = bt->next;
451
    }
452
 
453
    return list;
454
}
455
 
3 andreas 456
/*
457
 * Sort the button according to their Z-order.
458
 * The button with the highest Z-order will be the last button in the chain.
459
 * The algorithm is a bubble sort algorithm.
460
 */
461
bool TSubPage::sortButtons()
462
{
463
    DECL_TRACER("TSubPage::sortButtons()");
464
 
465
    bool turned = true;
466
 
467
    while (turned)
468
    {
469
        BUTTONS_T *button = mButtons;
470
        turned = false;
471
 
472
        while (button)
473
        {
474
            int zo = button->button->getZOrder();
475
 
476
            if (button->previous)
477
            {
478
                if (zo < button->previous->button->getZOrder())
479
                {
480
                    BUTTONS_T *pprev = button->previous->previous;
481
                    BUTTONS_T *prev = button->previous;
482
                    BUTTONS_T *next = button->next;
483
 
484
                    if (pprev)
485
                        pprev->next = button;
486
 
487
                    prev->next = next;
488
                    prev->previous = button;
489
                    button->next = prev;
490
                    button->previous = pprev;
491
 
492
                    if (!pprev)
493
                        mButtons = button;
494
 
495
                    button = next;
496
 
497
                    if (next)
498
                        next->previous = prev;
499
 
500
                    turned = true;
501
                    continue;
502
                }
503
            }
504
 
505
            button = button->next;
506
        }
507
    }
508
 
509
    return true;
510
}
11 andreas 511
 
31 andreas 512
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)
513
{
514
    DECL_TRACER("TSubPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)");
515
 
516
    int nw = mSubpage.width;
517
    int nh = mSubpage.height;
518
 
519
    if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
520
    {
521
        nw = (int)((double)mSubpage.width * gPageManager->getScaleFactor());
522
        nh = (int)((double)mSubpage.height * gPageManager->getScaleFactor());
523
    }
524
 
525
    switch (mSubpage.sr[0].jb)
526
    {
527
        case 0: // absolute position
528
            *left = mSubpage.sr[0].bx;
529
            *top = mSubpage.sr[0].by;
530
 
531
            if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
532
            {
533
                *left = (int)((double)mSubpage.sr[0].bx * gPageManager->getScaleFactor());
534
                *left = (int)((double)mSubpage.sr[0].by * gPageManager->getScaleFactor());
535
            }
536
        break;
537
 
538
        case 1: // top, left
539
            *left = 0;
540
            *top = 0;
541
        break;
542
 
543
        case 2: // center, top
544
            *left = (nw - im_width) / 2;
545
            *top = 0;
546
        break;
547
 
548
        case 3: // right, top
549
            *left = nw - im_width;
550
            *top = 0;
551
        break;
552
 
553
        case 4: // left, middle
554
            *left = 0;
555
            *top = (nh - im_height) / 2;
556
        break;
557
 
558
        case 6: // right, middle
559
            *left = nw - im_width;
560
            *top = (nh - im_height) / 2;
561
        break;
562
 
563
        case 7: // left, bottom
564
            *left = 0;
565
            *top = nh - im_height;
566
        break;
567
 
568
        case 8: // center, bottom
569
            *left = (nw - im_width) / 2;
570
            *top = nh - im_height;
571
        break;
572
 
573
        case 9: // right, bottom
574
            *left = nw - im_width;
575
            *top = nh - im_height;
576
        break;
577
 
578
        default:    // center middle
579
            *left = (nw - im_width) / 2;
580
            *top = (nh - im_height) / 2;
581
    }
582
 
583
    if (*left < 0)
584
        *left = 0;
585
 
586
    if (*top < 0)
587
        *top = 0;
588
}
589
 
11 andreas 590
RECT_T TSubPage::getRegion()
591
{
592
    DECL_TRACER("TSubPage::getRegion()");
593
    return {mSubpage.left, mSubpage.top, mSubpage.width, mSubpage.height};
594
}
595
 
15 andreas 596
/**
597
 * This method is called indirectly from the GUI after a mouse click. If This
598
 * subpage matches the clicked coordinates, than the elements are tested. If
599
 * an element is found that matches the coordinates it gets the click. It
600
 * depends on the kind of element what happens.
601
 */
11 andreas 602
void TSubPage::doClick(int x, int y, bool pressed)
603
{
604
    DECL_TRACER("TSubPage::doClick(int x, int y)");
605
 
606
    BUTTONS_T *button = mButtons;
607
    // Find last button
608
    while (button->next)
609
        button = button->next;
610
 
611
    // Scan in reverse order
612
    while (button)
613
    {
614
        TButton *but = button->button;
28 andreas 615
//        MSG_DEBUG("Button " << but->getButtonIndex() << ": lx=" << but->getLeftPosition() << ", ly=" << but->getTopPosition() << ", rx=" << (but->getLeftPosition() + but->getWidth()) << ", ry=" << (but->getTopPosition() + but->getHeight()));
11 andreas 616
 
31 andreas 617
        if (x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
618
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
11 andreas 619
        {
620
            MSG_DEBUG("Clicking button " << but->getButtonIndex() << ": " << but->getButtonName() << " to state " << (pressed ? "PRESS" : "RELEASE"));
15 andreas 621
            int btX = x - but->getLeftPosition();
622
            int btY = y - but->getTopPosition();
623
 
624
            if (but->doClick(btX, btY, pressed))
625
                break;
11 andreas 626
        }
627
 
628
        button = button->previous;
629
    }
630
}