Subversion Repositories tpanel

Rev

Rev 479 | Details | Compare with Previous | 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 <exception>
20
 
21
#include <include/core/SkFont.h>
22
#include <include/core/SkFontMetrics.h>
23
#include <include/core/SkTextBlob.h>
24
 
25
#include "tresources.h"
26
#include "texpat++.h"
27
#include "tpagemanager.h"
28
#include "tsubpage.h"
29
#include "tdrawimage.h"
486 andreas 30
#include "ttpinit.h"
446 andreas 31
#include "tconfig.h"
32
#include "terror.h"
33
#if TESTMODE == 1
34
#include "testmode.h"
35
#endif
36
 
37
#if __cplusplus < 201402L
38
#   error "This module requires at least C++14 standard!"
39
#else
40
#   if __cplusplus < 201703L
41
#       include <experimental/filesystem>
42
namespace fs = std::experimental::filesystem;
43
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
44
#   else
45
#       include <filesystem>
46
#       ifdef __ANDROID__
47
namespace fs = std::__fs::filesystem;
48
#       else
49
namespace fs = std::filesystem;
50
#       endif
51
#   endif
52
#endif
53
 
54
using std::string;
55
using std::vector;
56
using namespace Button;
57
using namespace Expat;
58
 
59
extern TPageManager *gPageManager;
60
 
61
TSubPage::TSubPage(const string& name)
62
    : mFile(name)
63
{
64
    DECL_TRACER("TSubPage::TSubPage(const string& path)");
65
    TError::clear();
66
 
449 andreas 67
    string projectPath = TConfig::getProjectPath();
446 andreas 68
 
69
    if (!fs::exists(projectPath + "/prj.xma"))
70
    {
71
        MSG_ERROR("Directory " << projectPath << " doesn't exist!");
72
        return;
73
    }
74
 
75
    string path = makeFileName(projectPath, name);
76
 
77
    if (isValidFile())
78
        mFName = getFileName();
79
    else
80
    {
81
        MSG_ERROR("Either the path \"" << projectPath << "\" or the file name \"" << name << "\" is invalid!");
82
        TError::setError();
83
        return;
84
    }
85
 
86
    if (gPageManager)
87
    {
88
        if (!_displayButton)
89
            _displayButton = gPageManager->getCallbackDB();
90
 
91
        if (!_setBackground)
92
            _setBackground = gPageManager->getCallbackBG();
93
 
94
        if (!_playVideo)
95
            _playVideo = gPageManager->getCallbackPV();
96
 
97
        if (!_callDropSubPage)
98
            _callDropSubPage = gPageManager->getCallDropSubPage();
99
    }
100
 
101
    initialize();
102
}
103
 
104
TSubPage::~TSubPage()
105
{
106
    DECL_TRACER("TSubPage::~TSubPage()");
107
 
108
    if (mSubpage.name.empty())
109
    {
110
        MSG_WARNING("Invalid page found!");
111
        return;
112
    }
113
 
114
    MSG_DEBUG("Destroing subpage " << mSubpage.pageID << ": " << mSubpage.name);
115
 
116
    BUTTONS_T *b = TPageInterface::getButtons();
117
    BUTTONS_T *next = nullptr;
118
 
119
    while (b)
120
    {
121
        next = b->next;
122
 
123
        if (b->button)
124
        {
125
            // If this is a system button, we must remove it first from the list
126
            dropButton(b->button);
127
            delete b->button;
128
        }
129
 
130
        delete b;
131
        b = next;
132
    }
133
 
134
    setButtons(nullptr);
135
}
136
 
137
void TSubPage::initialize()
138
{
139
    DECL_TRACER("TSubPage::initialize()");
140
 
141
    if (mFName.empty())
142
        return;
143
 
144
    TError::clear();
145
    TExpat xml(mFName);
146
 
486 andreas 147
    if (!TTPInit::isTP5())
148
        xml.setEncoding(ENC_CP1250);
149
    else
150
        xml.setEncoding(ENC_UTF8);
151
 
446 andreas 152
    if (!xml.parse())
153
        return;
154
 
155
    int depth = 0;
156
    size_t index = 0;
157
    size_t oldIndex = 0;
158
    string ename, content;
159
    vector<ATTRIBUTE_t> attrs;
160
 
161
    if ((index = xml.getElementIndex("page", &depth)) == TExpat::npos)
162
    {
163
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found! Invalid XML file!");
164
        TError::setError();
165
        return;
166
    }
167
 
168
    attrs = xml.getAttributes();
169
    string stype = xml.getAttribute("type", attrs);
170
 
171
    if (stype.compare("subpage") != 0)
172
    {
173
        MSG_ERROR("The type " << stype << " is invalid for a subpage!");
174
        TError::setError();
175
        return;
176
    }
177
 
178
    mSubpage.popupType = xml.getAttribute("popupType", attrs);  // popup or subpage
179
 
180
    while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
181
    {
182
        if (ename.compare("pageID") == 0)
183
            mSubpage.pageID = xml.convertElementToInt(content);
184
        else if (ename.compare("name") == 0)
185
            mSubpage.name = content;
186
        else if (ename.compare("left") == 0)
187
        {
188
            mSubpage.left = xml.convertElementToInt(content);
189
            mSubpage.leftOrig = mSubpage.left;
190
        }
191
        else if (ename.compare("top") == 0)
192
        {
193
            mSubpage.top = xml.convertElementToInt(content);
194
            mSubpage.topOrig = mSubpage.top;
195
        }
196
        else if (ename.compare("width") == 0)
197
        {
198
            mSubpage.width = xml.convertElementToInt(content);
199
            mSubpage.widthOrig = mSubpage.width;
200
        }
201
        else if (ename.compare("height") == 0)
202
        {
203
            mSubpage.height = xml.convertElementToInt(content);
204
            mSubpage.heightOrig = mSubpage.height;
205
        }
206
        else if (ename.compare("group") == 0)
207
            mSubpage.group = content;
208
        else if (ename.compare("modal") == 0)
209
            mSubpage.modal = xml.convertElementToInt(content);
210
        else if (ename.compare("showEffect") == 0)
211
            mSubpage.showEffect = (SHOWEFFECT)xml.convertElementToInt(content);
212
        else if (ename.compare("showTime") == 0)
213
            mSubpage.showTime = xml.convertElementToInt(content);
214
        else if (ename.compare("hideTime") == 0)
215
            mSubpage.hideTime = xml.convertElementToInt(content);
216
        else if (ename.compare("hideEffect") == 0)
217
            mSubpage.hideEffect = (SHOWEFFECT)xml.convertElementToInt(content);
218
        else if (ename.compare("timeout") == 0)
219
            mSubpage.timeout = xml.convertElementToInt(content);
220
        else if (ename.compare("resetPos") == 0)
221
            mSubpage.resetPos = xml.convertElementToInt(content);
222
        else if (ename.compare("button") == 0)      // Read a button
223
        {
224
            try
225
            {
226
                TButton *button = new TButton();
227
                TPageInterface::registerListCallback<TSubPage>(button, this);
228
                button->setPalette(mPalette);
229
                button->setFonts(getFonts());
230
                index = button->initialize(&xml, index);
231
                button->setParentSize(mSubpage.width, mSubpage.height);
232
                button->registerCallback(_displayButton);
233
                button->regCallPlayVideo(_playVideo);
234
 
235
                if (TError::isError())
236
                {
237
                    MSG_ERROR("Dropping button because of previous errors!");
238
                    delete button;
239
                    return;
240
                }
241
 
242
                button->setHandle(((mSubpage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
243
                button->createButtons();
244
                addButton(button);
486 andreas 245
                string sType1 = xml.getElementTypeStr(index);
246
                string sType2 = xml.getElementTypeStr(index+1);
247
                MSG_DEBUG("Element type 1: " << sType1);
248
                MSG_DEBUG("Element type 2: " << sType2);
249
 
250
                if (xml.isElementTypeEnd(index+1))
251
                    index++;        // Jump over the end tag of the button.
446 andreas 252
            }
253
            catch (std::exception& e)
254
            {
255
                MSG_ERROR("Memory exception: " << e.what());
256
                TError::setError();
257
                return;
258
            }
259
        }
260
        else if (ename.compare("sr") == 0)
261
        {
262
            SR_T sr;
263
            sr.number = xml.getAttributeInt("number", attrs);
264
 
265
            while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
266
            {
267
                if (ename.compare("bs") == 0)
268
                    sr.bs = content;
269
                else if (ename.compare("cb") == 0)
270
                    sr.cb = content;
271
                else if (ename.compare("cf") == 0)
272
                    sr.cf = content;
273
                else if (ename.compare("ct") == 0)
274
                    sr.ct = content;
275
                else if (ename.compare("ec") == 0)
276
                    sr.ec = content;
277
                else if (ename.compare("bm") == 0)
278
                    sr.bm = content;
279
                else if (ename.compare("mi") == 0)
280
                    sr.mi = content;
281
                else if (ename.compare("ji") == 0)
282
                    sr.ji = xml.convertElementToInt(content);
283
                else if (ename.compare("jb") == 0)
284
                    sr.jb = xml.convertElementToInt(content);
285
                else if (ename.compare("fi") == 0)
286
                    sr.fi = xml.convertElementToInt(content);
287
                else if (ename.compare("ii") == 0)
288
                    sr.ii = xml.convertElementToInt(content);
289
                else if (ename.compare("ix") == 0)
290
                    sr.ix = xml.convertElementToInt(content);
291
                else if (ename.compare("iy") == 0)
292
                    sr.iy = xml.convertElementToInt(content);
293
                else if (ename.compare("oo") == 0)
294
                    sr.oo = xml.convertElementToInt(content);
295
                else if (ename.compare("te") == 0)
296
                    sr.te = content;
297
                else if (ename.compare("tx") == 0)
298
                    sr.tx = xml.convertElementToInt(content);
299
                else if (ename.compare("ty") == 0)
300
                    sr.ty = xml.convertElementToInt(content);
301
                else if (ename.compare("et") == 0)
302
                    sr.et = xml.convertElementToInt(content);
303
                else if (ename.compare("ww") == 0)
304
                    sr.ww = xml.convertElementToInt(content);
305
                else if (ename.compare("jt") == 0)
479 andreas 306
                    sr.jt = (Button::ORIENTATION)xml.convertElementToInt(content);
486 andreas 307
                else if (ename.compare("bitmapEntry") == 0)
308
                {
309
                    string fname;
310
                    BITMAPS_t bitmapEntry;
311
                    MSG_DEBUG("Section: " << ename);
446 andreas 312
 
486 andreas 313
                    while ((index = xml.getNextElementFromIndex(index, &fname, &content, &attrs)) != TExpat::npos)
314
                    {
315
                        if (fname.compare("fileName") == 0)
316
                            bitmapEntry.fileName = content;
317
                        else if (fname.compare("justification") == 0)
318
                            bitmapEntry.justification = static_cast<ORIENTATION>(xml.convertElementToInt(content));
319
                        else if (fname.compare("offsetX") == 0)
320
                            bitmapEntry.offsetX = xml.convertElementToInt(content);
321
                        else if (fname.compare("offsetY") == 0)
322
                            bitmapEntry.offsetY = xml.convertElementToInt(content);
323
 
324
                        oldIndex = index;
325
                    }
326
 
327
                    MSG_DEBUG("Found image: " << bitmapEntry.fileName << ", justification: " << bitmapEntry.justification << ", Offset: " << bitmapEntry.offsetX << "x" << bitmapEntry.offsetY);
328
                    sr.bitmaps.push_back(bitmapEntry);
329
 
330
                    if (index == TExpat::npos)
331
                        index = oldIndex + 1;
332
                }
333
 
446 andreas 334
                oldIndex = index;
335
            }
336
 
337
            mSubpage.sr.push_back(sr);
338
        }
339
 
340
        if (index == TExpat::npos)
341
            index = oldIndex + 1;
342
    }
343
 
344
    setSR(mSubpage.sr);
345
/*
346
    if (TStreamError::checkFilter(HLOG_DEBUG))
347
    {
348
        MSG_DEBUG("PageID: " << mSubpage.pageID);
349
        MSG_DEBUG("Name  : " << mSubpage.name);
350
        MSG_DEBUG("Left  : " << mSubpage.left);
351
        MSG_DEBUG("Top   : " << mSubpage.top);
352
        MSG_DEBUG("Width : " << mSubpage.width);
353
        MSG_DEBUG("Height: " << mSubpage.height);
354
 
355
        vector<SR_T>::iterator iter;
356
        size_t pos = 1;
357
 
358
        for (iter = mSubpage.sr.begin(); iter != mSubpage.sr.end(); ++iter)
359
        {
360
            MSG_DEBUG("   " << pos << ": id: " << iter->number);
361
            MSG_DEBUG("   " << pos << ": bs: " << iter->bs);
362
            MSG_DEBUG("   " << pos << ": cb: " << iter->cb);
363
            MSG_DEBUG("   " << pos << ": cf: " << iter->cf);
364
            MSG_DEBUG("   " << pos << ": ct: " << iter->ct);
365
            MSG_DEBUG("   " << pos << ": ec: " << iter->ec);
366
            MSG_DEBUG("   " << pos << ": bm: " << iter->bm);
367
            MSG_DEBUG("   " << pos << ": mi: " << iter->mi);
368
            MSG_DEBUG("   " << pos << ": fi: " << iter->fi);
369
            pos++;
370
        }
371
    }
372
*/
373
    // Here the sort function could be called. But it's not necessary because
374
    // the buttons are stored in ascending Z order. Therefor the following
375
    // method call is commented out.
376
    // sortButtons();
377
}
378
 
379
void TSubPage::show()
380
{
381
    DECL_TRACER("TSubPage::show()");
382
 
383
    if (!_setBackground)
384
    {
385
        if (gPageManager && gPageManager->getCallbackBG())
386
            _setBackground = gPageManager->getCallbackBG();
387
        else
388
        {
389
            MSG_WARNING("No callback \"setBackground\" was set!");
390
#if TESTMODE == 1
391
            setScreenDone();
392
#endif
393
            return;
394
        }
395
    }
396
 
486 andreas 397
    bool noSr = mSubpage.sr.empty();
446 andreas 398
    bool haveImage = false;
399
    ulong handle = (mSubpage.pageID << 16) & 0xffff0000;
400
    MSG_DEBUG("Processing subpage " << mSubpage.pageID << ": " << mSubpage.name);
401
    SkBitmap target;
402
 
403
    if (!allocPixels(mSubpage.width, mSubpage.height, &target))
404
    {
405
#if TESTMODE == 1
406
        setScreenDone();
407
#endif
408
        return;
409
    }
410
 
411
    if (mSubpage.resetPos != 0)
412
    {
413
        mSubpage.left = mSubpage.leftOrig;
414
        mSubpage.top = mSubpage.topOrig;
415
        mSubpage.width = mSubpage.widthOrig;
416
        mSubpage.height = mSubpage.heightOrig;
417
    }
418
 
486 andreas 419
    if (!noSr)
420
        target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
421
    else
422
        target.eraseColor(SK_ColorTRANSPARENT);
423
 
424
    MSG_DEBUG("Number TP5 images: " << mSubpage.sr[0].bitmaps.size());
446 andreas 425
    // Draw the background, if any
486 andreas 426
    if (!noSr && (!mSubpage.sr[0].bm.empty() || !mSubpage.sr[0].mi.empty() || !mSubpage.sr[0].bitmaps.empty()))
446 andreas 427
    {
428
        TDrawImage dImage;
429
        dImage.setWidth(mSubpage.width);
430
        dImage.setHeight(mSubpage.height);
486 andreas 431
        dImage.setSr(mSubpage.sr);
446 andreas 432
 
486 andreas 433
        if (!TTPInit::isTP5() && !mSubpage.sr[0].bm.empty())
446 andreas 434
        {
435
            MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
436
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
437
            SkBitmap bm;
438
 
439
            if (rawImage)
440
            {
441
                MSG_DEBUG("Decoding image BM ...");
442
 
443
                if (!DecodeDataToBitmap(rawImage, &bm))
444
                {
445
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
446
                }
447
                else if (!bm.empty())
448
                {
449
                    dImage.setImageBm(bm);
450
                    SkImageInfo info = bm.info();
451
                    mSubpage.sr[0].bm_width = info.width();
452
                    mSubpage.sr[0].bm_height = info.height();
453
                    haveImage = true;
454
                }
455
                else
456
                {
457
                    MSG_WARNING("BM image " << mSubpage.sr[0].bm << " seems to be empty!");
458
                }
459
            }
460
        }
486 andreas 461
        else if (TTPInit::isTP5() && mSubpage.sr[0].bitmaps.size() > 0)
462
        {
463
            MSG_DEBUG("Loading TP5 image(s) ...");
464
            vector<Button::BITMAPS_t>::iterator iter;
446 andreas 465
 
486 andreas 466
            for (iter = mSubpage.sr[0].bitmaps.begin(); iter != mSubpage.sr[0].bitmaps.end(); ++iter)
467
            {
468
                sk_sp<SkData> rawImage = readImage(iter->fileName);
469
                SkBitmap bm;
470
 
471
                if (rawImage)
472
                {
473
                    MSG_DEBUG("Decoding image BM ...");
474
 
475
                    if (!DecodeDataToBitmap(rawImage, &bm))
476
                    {
477
                        MSG_WARNING("Problem while decoding image " << iter->fileName);
478
                    }
479
                    else if (!bm.empty())
480
                    {
481
                        dImage.setImageBm(bm);
482
                        SkImageInfo info = bm.info();
483
                        haveImage = true;
484
                    }
485
                    else
486
                    {
487
                        MSG_WARNING("BM image " << iter->fileName << " seems to be empty!");
488
                    }
489
                }
490
            }
491
        }
492
 
446 andreas 493
        if (!mSubpage.sr[0].mi.empty())
494
        {
495
            MSG_DEBUG("Loading image " << mSubpage.sr[0].mi);
496
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].mi);
497
            SkBitmap mi;
498
 
499
            if (rawImage)
500
            {
501
                MSG_DEBUG("Decoding image MI ...");
502
 
503
                if (!DecodeDataToBitmap(rawImage, &mi))
504
                {
505
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].mi);
506
                }
507
                else if (!mi.empty())
508
                {
509
                    dImage.setImageMi(mi);
510
                    SkImageInfo info = mi.info();
511
                    mSubpage.sr[0].mi_width = info.width();
512
                    mSubpage.sr[0].mi_height = info.height();
513
                    haveImage = true;
514
                }
515
                else
516
                {
517
                    MSG_WARNING("MI image " << mSubpage.sr[0].mi << " seems to be empty!");
518
                }
519
            }
520
        }
521
 
522
        if (haveImage)
523
        {
524
            dImage.setSr(mSubpage.sr);
525
 
526
            if (!dImage.drawImage(&target))
527
                return;
528
 
529
            if (!mSubpage.sr[0].te.empty())
530
            {
531
                if (!drawText(mSubpage, &target))
532
                    return;
533
            }
534
#ifdef _OPAQUE_SKIA_
535
            if (mSubpage.sr[0].oo < 255 && mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
536
                setOpacity(&target, mSubpage.sr[0].oo);
537
#endif
538
 
539
#ifdef _SCALE_SKIA_
540
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
541
            {
542
                SkPaint paint;
543
                int left, top;
544
                SkImageInfo info = target.info();
545
 
546
                paint.setBlendMode(SkBlendMode::kSrc);
547
                paint.setFilterQuality(kHigh_SkFilterQuality);
548
                // Calculate new dimension
549
                double scaleFactor = gPageManager->getScaleFactor();
550
                MSG_DEBUG("Using scale factor " << scaleFactor);
551
                int lwidth = (int)((double)info.width() * scaleFactor);
552
                int lheight = (int)((double)info.height() * scaleFactor);
553
                int twidth = (int)((double)mSubpage.width * scaleFactor);
554
                int theight = (int)((double)mSubpage.height * scaleFactor);
555
                calcPosition(lwidth, lheight, &left, &top);
556
                // Create a canvas and draw new image
557
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
558
 
559
                if (!allocPixels(twidth, theight, &target))
560
                {
561
#if TESTMODE == 1
562
                    setScreenDone();
563
#endif
564
                    return;
565
                }
566
 
567
                target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
568
                SkCanvas can(target, SkSurfaceProps());
569
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
570
                can.drawImageRect(im, rect, &paint);
571
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
572
            }
573
#endif
574
#ifdef _OPAQUE_SKIA_
575
            if (mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
576
            {
577
                TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
578
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
579
            }
580
#else
581
            if (mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
582
            {
583
                TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
584
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
585
            }
586
#endif
587
        }
588
    }
589
 
486 andreas 590
    if (!noSr && !mSubpage.sr[0].te.empty())
446 andreas 591
    {
592
        MSG_DEBUG("Drawing a text only on background image ...");
593
 
594
        if (drawText(mSubpage, &target))
595
            haveImage = true;
596
    }
597
 
598
    // Check for a frame and draw it if there is one.
486 andreas 599
    if (!noSr && !mSubpage.sr[0].bs.empty())
446 andreas 600
    {
601
        if (drawFrame(mSubpage, &target))
602
            haveImage = true;
603
    }
604
 
486 andreas 605
    if (!noSr && haveImage)
446 andreas 606
    {
607
#ifdef _OPAQUE_SKIA_
608
        if (mSubpage.sr[0].oo < 255)
609
            setOpacity(&target, mSubpage.sr[0].oo);
610
#endif
611
        TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
612
#ifdef _OPAQUE_SKIA_
613
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
614
#else
615
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
616
#endif
617
    }
486 andreas 618
    else if (!noSr && !haveImage)
446 andreas 619
    {
620
        MSG_DEBUG("Calling \"setBackground\" with no image ...");
621
#ifdef _OPAQUE_SKIA_
622
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(mSubpage.sr[0].cf));
623
#else
624
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
625
#endif
626
    }
627
 
628
    // Draw the buttons
629
    BUTTONS_T *button = TPageInterface::getButtons();
630
 
631
    while (button)
632
    {
633
        if (button->button)
634
        {
635
            MSG_DEBUG("Drawing button " << handleToString(button->button->getHandle()) << ": " << button->button->getButtonIndex() << " --> " << button->button->getButtonName());
636
            TPageInterface::registerListCallback<TSubPage>(button->button, this);
637
            button->button->registerCallback(_displayButton);
638
            button->button->regCallPlayVideo(_playVideo);
639
            button->button->setFonts(getFonts());
640
            button->button->setPalette(mPalette);
641
            button->button->createButtons();
642
 
486 andreas 643
            if (!noSr)
446 andreas 644
                button->button->setGlobalOpacity(mSubpage.sr[0].oo);
645
 
646
            if (mSubpage.resetPos != 0)
647
                button->button->resetButton();
648
 
649
            button->button->show();
650
        }
651
 
652
        button = button->next;
653
    }
654
 
655
    // Mark page as visible
656
    mVisible = true;
657
 
658
    if (gPageManager && gPageManager->getPageFinished())
659
        gPageManager->getPageFinished()(handle);
660
}
661
 
662
SkBitmap& TSubPage::getBgImage()
663
{
664
    DECL_TRACER("TSubPage::getBgImage()");
665
 
666
    if (!mBgImage.empty())
667
        return mBgImage;
668
 
669
    bool haveImage = false;
670
    MSG_DEBUG("Creating image for subpage " << mSubpage.pageID << ": " << mSubpage.name);
671
    SkBitmap target;
672
 
673
    if (!allocPixels(mSubpage.width, mSubpage.height, &target))
674
        return mBgImage;
675
 
676
    target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
677
    // Draw the background, if any
678
    if (mSubpage.sr.size() > 0 && (!mSubpage.sr[0].bm.empty() || !mSubpage.sr[0].mi.empty()))
679
    {
680
        TDrawImage dImage;
681
        dImage.setWidth(mSubpage.width);
682
        dImage.setHeight(mSubpage.height);
486 andreas 683
        dImage.setSr(mSubpage.sr);
446 andreas 684
 
685
        if (!mSubpage.sr[0].bm.empty())
686
        {
687
            MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
688
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
689
            SkBitmap bm;
690
 
691
            if (rawImage)
692
            {
693
                MSG_DEBUG("Decoding image BM ...");
694
 
695
                if (!DecodeDataToBitmap(rawImage, &bm))
696
                {
697
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
698
                }
699
                else if (!bm.empty())
700
                {
701
                    dImage.setImageBm(bm);
702
                    SkImageInfo info = bm.info();
703
                    mSubpage.sr[0].bm_width = info.width();
704
                    mSubpage.sr[0].bm_height = info.height();
705
                    haveImage = true;
706
                }
707
                else
708
                {
709
                    MSG_WARNING("BM image " << mSubpage.sr[0].bm << " seems to be empty!");
710
                }
711
            }
712
        }
713
 
714
        if (!mSubpage.sr[0].mi.empty())
715
        {
716
            MSG_DEBUG("Loading image " << mSubpage.sr[0].mi);
717
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].mi);
718
            SkBitmap mi;
719
 
720
            if (rawImage)
721
            {
722
                MSG_DEBUG("Decoding image MI ...");
723
 
724
                if (!DecodeDataToBitmap(rawImage, &mi))
725
                {
726
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].mi);
727
                }
728
                else if (!mi.empty())
729
                {
730
                    dImage.setImageMi(mi);
731
                    SkImageInfo info = mi.info();
732
                    mSubpage.sr[0].mi_width = info.width();
733
                    mSubpage.sr[0].mi_height = info.height();
734
                    haveImage = true;
735
                }
736
                else
737
                {
738
                    MSG_WARNING("MI image " << mSubpage.sr[0].mi << " seems to be empty!");
739
                }
740
            }
741
        }
742
 
743
        if (haveImage)
744
        {
745
            dImage.setSr(mSubpage.sr);
746
 
747
            if (!dImage.drawImage(&target))
748
                return mBgImage;
749
 
750
            if (!mSubpage.sr[0].te.empty())
751
            {
752
                if (!drawText(mSubpage, &target))
753
                    return mBgImage;
754
            }
755
 
756
            if (mSubpage.sr[0].oo < 255 && mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
757
                setOpacity(&target, mSubpage.sr[0].oo);
758
 
759
#ifdef _SCALE_SKIA_
760
            size_t rowBytes = info.minRowBytes();
761
            size_t size = info.computeByteSize(rowBytes);
762
            SkImageInfo info = target.info();
763
 
764
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
765
            {
766
                SkPaint paint;
767
                int left, top;
768
 
769
                paint.setBlendMode(SkBlendMode::kSrc);
770
                paint.setFilterQuality(kHigh_SkFilterQuality);
771
                // Calculate new dimension
772
                double scaleFactor = gPageManager->getScaleFactor();
773
                MSG_DEBUG("Using scale factor " << scaleFactor);
774
                int lwidth = (int)((double)info.width() * scaleFactor);
775
                int lheight = (int)((double)info.height() * scaleFactor);
776
                int twidth = (int)((double)mSubpage.width * scaleFactor);
777
                int theight = (int)((double)mSubpage.height * scaleFactor);
778
                calcPosition(lwidth, lheight, &left, &top);
779
                // Create a canvas and draw new image
780
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
781
 
782
                if (!allocPixels(twidth, theight, &target))
783
                    return;
784
 
785
                target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
786
                SkCanvas can(target, SkSurfaceProps());
787
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
788
                can.drawImageRect(im, rect, &paint);
789
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
790
            }
791
#endif
792
        }
793
    }
794
 
795
    if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].te.empty())
796
    {
797
        MSG_DEBUG("Drawing a text only on background image ...");
798
 
799
        if (drawText(mSubpage, &target))
800
            haveImage = true;
801
    }
802
 
803
    // Check for a frame and draw it if there is one.
804
    if (!mSubpage.sr[0].bs.empty())
805
    {
806
        if (!drawBorder(&target, mSubpage.sr[0].bs, mSubpage.width, mSubpage.height, mSubpage.sr[0].cb))
807
        {
808
            if (drawFrame(mSubpage, &target))
809
                haveImage = true;
810
        }
811
    }
812
 
813
    if (haveImage)
814
    {
815
        if (mSubpage.sr[0].oo < 255)
816
            setOpacity(&target, mSubpage.sr[0].oo);
817
 
818
        mBgImage = target;
819
    }
820
 
821
    return mBgImage;
822
}
823
 
824
void TSubPage::drop()
825
{
826
    DECL_TRACER("TSubPage::drop()");
827
 
828
    stopTimer();
829
 
830
    if (mVisible && _callDropSubPage)
831
        _callDropSubPage((mSubpage.pageID << 16) & 0xffff0000, mParent);
832
#if TESTMODE == 1
833
    else
834
    {
835
        __success = true;
836
        setScreenDone();
837
    }
838
#endif
839
 
840
    // Set all elements of subpage invisible
841
    BUTTONS_T *bt = TPageInterface::getButtons();
842
 
843
    while (bt)
844
    {
845
        bt->button->hide();
846
        bt = bt->next;
847
    }
848
 
849
    mZOrder = -1;
850
    mVisible = false;
851
}
852
 
853
void TSubPage::startTimer()
854
{
855
    DECL_TRACER("TSubPage::startTimer()");
856
 
857
    if (mSubpage.timeout <= 0 || mTimerRunning)
858
        return;
859
 
860
    try
861
    {
862
        mThreadTimer = std::thread([=] { runTimer(); });
863
        mThreadTimer.detach();
864
    }
865
    catch (std::exception& e)
866
    {
867
        MSG_ERROR("Error starting a timeout thread: " << e.what());
868
        return;
869
    }
870
}
871
 
872
void TSubPage::runTimer()
873
{
874
    DECL_TRACER("TSubPage::runTimer()");
875
 
876
    if (mTimerRunning)
877
        return;
878
 
879
    mTimerRunning = true;
880
    ulong tm = mSubpage.timeout * 100;
881
    ulong unit = 100;
882
    ulong total = 0;
883
 
884
    while (mTimerRunning && !prg_stopped && total < tm)
885
    {
886
        std::this_thread::sleep_for(std::chrono::milliseconds(unit));
887
        total += unit;
888
    }
889
 
890
    drop();
891
    mTimerRunning = false;
892
}
893
 
894
#ifdef _SCALE_SKIA_
895
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)
896
#else
897
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)
898
#endif
899
{
900
    DECL_TRACER("TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)");
901
 
902
    int nw = mSubpage.width;
903
    int nh = mSubpage.height;
904
#ifdef _SCALE_SKIA_
905
    if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
906
    {
907
        nw = (int)((double)mSubpage.width * gPageManager->getScaleFactor());
908
        nh = (int)((double)mSubpage.height * gPageManager->getScaleFactor());
909
    }
910
#endif
911
    switch (mSubpage.sr[0].jb)
912
    {
913
        case 0: // absolute position
914
            *left = mSubpage.sr[0].bx;
915
            *top = mSubpage.sr[0].by;
916
#ifdef _SCALE_SKIA_
917
            if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
918
            {
919
                *left = (int)((double)mSubpage.sr[0].bx * gPageManager->getScaleFactor());
920
                *left = (int)((double)mSubpage.sr[0].by * gPageManager->getScaleFactor());
921
            }
922
#endif
923
        break;
924
 
925
        case 1: // top, left
926
            *left = 0;
927
            *top = 0;
928
        break;
929
 
930
        case 2: // center, top
931
            *left = (nw - im_width) / 2;
932
            *top = 0;
933
        break;
934
 
935
        case 3: // right, top
936
            *left = nw - im_width;
937
            *top = 0;
938
        break;
939
 
940
        case 4: // left, middle
941
            *left = 0;
942
            *top = (nh - im_height) / 2;
943
        break;
944
 
945
        case 6: // right, middle
946
            *left = nw - im_width;
947
            *top = (nh - im_height) / 2;
948
        break;
949
 
950
        case 7: // left, bottom
951
            *left = 0;
952
            *top = nh - im_height;
953
        break;
954
 
955
        case 8: // center, bottom
956
            *left = (nw - im_width) / 2;
957
            *top = nh - im_height;
958
        break;
959
 
960
        case 9: // right, bottom
961
            *left = nw - im_width;
962
            *top = nh - im_height;
963
        break;
964
 
965
        default:    // center middle
966
            *left = (nw - im_width) / 2;
967
            *top = (nh - im_height) / 2;
968
    }
969
 
970
    if (*left < 0)
971
        *left = 0;
972
 
973
    if (*top < 0)
974
        *top = 0;
975
}
976
 
977
RECT_T TSubPage::getRegion()
978
{
979
    DECL_TRACER("TSubPage::getRegion()");
980
    return {mSubpage.left, mSubpage.top, mSubpage.width, mSubpage.height};
981
}
982
 
983
/**
984
 * This method is called indirectly from the GUI after a mouse click. If This
985
 * subpage matches the clicked coordinates, than the elements are tested. If
986
 * an element is found that matches the coordinates it gets the click. It
987
 * depends on the kind of element what happens.
988
 */
989
void TSubPage::doClick(int x, int y, bool pressed)
990
{
991
    DECL_TRACER("TSubPage::doClick(int x, int y)");
992
 
993
    BUTTONS_T *button = TPageInterface::getButtons();
994
 
995
    if (!button)
996
        return;
997
 
998
    // Find last button
999
    while (button && button->next)
1000
        button = button->next;
1001
 
1002
    // Scan in reverse order
1003
    while (button)
1004
    {
1005
        TButton *but = button->button;
1006
        bool clickable = but->isClickable();
1007
        MSG_DEBUG("Testing button " << but->getButtonIndex() << " (" << but->getButtonName() << "): " << (clickable ? "CLICKABLE" : "NOT CLICKABLE"));
1008
 
1009
        if (clickable && x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
1010
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
1011
        {
1012
            MSG_DEBUG("Clicking button " << but->getButtonIndex() << ": " << but->getButtonName() << " to state " << (pressed ? "PRESS" : "RELEASE"));
1013
            int btX = x - but->getLeftPosition();
1014
            int btY = y - but->getTopPosition();
1015
 
1016
            if (but->doClick(btX, btY, pressed))
1017
                break;
1018
        }
1019
 
1020
        button = button->previous;
1021
    }
1022
}
1023
 
1024
void TSubPage::moveMouse(int x, int y)
1025
{
1026
    DECL_TRACER("TSubPage::moveMouse(int x, int y)");
1027
 
1028
    BUTTONS_T *button = TPageInterface::getButtons();
1029
 
1030
    if (!button)
1031
        return;
1032
 
1033
    // Find last button
1034
    while (button && button->next)
1035
        button = button->next;
1036
 
1037
    // Scan in reverse order
1038
    while (button)
1039
    {
1040
        TButton *but = button->button;
1041
 
1042
        if (but->getButtonType() != BARGRAPH && but->getButtonType() != JOYSTICK)
1043
        {
1044
            button = button->previous;
1045
            continue;
1046
        }
1047
 
1048
        bool clickable = but->isClickable();
1049
 
1050
        if (clickable && x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
1051
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
1052
        {
1053
            int btX = x - but->getLeftPosition();
1054
            int btY = y - but->getTopPosition();
1055
 
1056
            if (but->getButtonType() == BARGRAPH)
1057
                but->moveBargraphLevel(btX, btY);
1058
            else if (but->getButtonType() == JOYSTICK && !but->getLevelFuction().empty())
1059
            {
1060
                but->drawJoystick(btX, btY);
1061
                but->sendJoystickLevels();
1062
            }
1063
 
1064
            break;
1065
        }
1066
 
1067
        button = button->previous;
1068
    }
1069
}