Subversion Repositories tpanel

Rev

Rev 446 | Rev 479 | Go to most recent revision | 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 <sys/types.h>
20
#include <sys/stat.h>
21
#include <unistd.h>
22
#ifdef __ANDROID__
23
#include <android/log.h>
24
#endif
25
 
26
#include <include/core/SkFont.h>
27
#include <include/core/SkFontMetrics.h>
28
#include <include/core/SkTextBlob.h>
29
 
30
#include "tlock.h"
31
#include "tresources.h"
32
#include "tpagemanager.h"
33
#include "tpage.h"
34
#include "tdrawimage.h"
35
#include "texpat++.h"
36
#include "tconfig.h"
37
#include "terror.h"
38
#if TESTMODE == 1
39
#include "testmode.h"
40
#endif
41
 
42
#if __cplusplus < 201402L
43
#   error "This module requires at least C++14 standard!"
44
#else
45
#   if __cplusplus < 201703L
46
#       include <experimental/filesystem>
47
namespace fs = std::experimental::filesystem;
48
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
49
#   else
50
#       include <filesystem>
51
#       ifdef __ANDROID__
52
namespace fs = std::__fs::filesystem;
53
#       else
54
namespace fs = std::filesystem;
55
#       endif
56
#   endif
57
#endif
58
 
59
using std::string;
60
using std::vector;
61
using std::map;
62
using std::pair;
63
using namespace Button;
64
using namespace Expat;
65
 
66
extern TPageManager *gPageManager;
67
 
68
TPage::TPage(const string& name)
69
{
70
    DECL_TRACER("TPage::TPage(const string& name)");
71
    TError::clear();
72
 
73
    if (gPageManager)
74
    {
75
        if (!_setBackground)
76
            _setBackground = gPageManager->getCallbackBG();
77
 
78
        if (!_displayButton)
79
            _displayButton = gPageManager->getCallbackDB();
80
 
81
        if (!_callDropPage)
82
            _callDropPage = gPageManager->getCallDropPage();
83
 
84
        if (!_callDropSubPage)
85
            _callDropSubPage = gPageManager->getCallDropSubPage();
86
 
87
        if (!_playVideo)
88
            _playVideo = gPageManager->getCallbackPV();
89
    }
90
 
91
    if (name.compare("_progress") == 0)
92
    {
93
        addProgress();
94
        return ;
95
    }
96
 
97
    initialize(name);
98
}
99
 
100
TPage::~TPage()
101
{
102
    DECL_TRACER("TPage::~TPage()");
103
 
104
    MSG_DEBUG("Destroing page " << mPage.pageID << ": " << mPage.name);
105
    BUTTONS_T *p = getButtons();
106
    BUTTONS_T *next = nullptr;
107
 
108
    while (p)
109
    {
110
        next = p->next;
111
        delete p->button;
112
        delete p;
113
        p = next;
114
    }
115
 
116
    setButtons(nullptr);
117
    mSubPages.clear();
118
}
119
 
120
void TPage::initialize(const string& nm)
121
{
122
    DECL_TRACER("TPage::initialize(const string& name)");
123
 
449 andreas 124
    string projectPath = TConfig::getProjectPath();
446 andreas 125
 
126
    if (!fs::exists(projectPath + "/prj.xma"))
127
    {
128
        MSG_ERROR("Directory " << projectPath << " doesn't exist!");
129
        return;
130
    }
131
 
132
    makeFileName(projectPath, nm);
133
 
134
    MSG_DEBUG("Using path: " << projectPath << " and file: " << nm);
135
 
136
    if (isValidFile())
137
        mPath = getFileName();
138
 
139
    TExpat xml(mPath);
140
    xml.setEncoding(ENC_CP1250);
141
 
142
    if (!xml.parse())
143
        return;
144
 
145
    int depth = 0;
146
    size_t index = 0;
147
    size_t oldIndex = 0;
148
    vector<ATTRIBUTE_t> attrs;
149
 
150
    if ((index = xml.getElementIndex("page", &depth)) == TExpat::npos)
151
    {
152
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found!");
153
        TError::setError();
154
        return;
155
    }
156
 
157
    attrs = xml.getAttributes();
158
    string type = xml.getAttribute("type", attrs);
159
 
160
    if (type.compare("page") != 0)
161
    {
162
        MSG_ERROR("Invalid page type \"" << type << "\"!");
163
        TError::setError();
164
        return;
165
    }
166
 
167
    depth++;
168
    string ename, content;
169
 
170
    while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
171
    {
172
        string e = ename;
173
 
174
        if (e.compare("pageID") == 0)
175
            mPage.pageID = xml.convertElementToInt(content);
176
        else if (e.compare("name") == 0)
177
            mPage.name = content;
178
        else if (e.compare("width") == 0)
179
            mPage.width = xml.convertElementToInt(content);
180
        else if (e.compare("height") == 0)
181
            mPage.height = xml.convertElementToInt(content);
182
        else if (e.compare("button") == 0)
183
        {
184
            TButton *button = new TButton();
185
            TPageInterface::registerListCallback<TPage>(button, this);
186
 
187
            button->setPalette(mPalette);
188
            button->setFonts(getFonts());
189
            button->registerCallback(_displayButton);
190
            button->regCallPlayVideo(_playVideo);
191
            index = button->initialize(&xml, index);
192
            button->setParentSize(mPage.width, mPage.height);
193
 
194
            if (TError::isError())
195
            {
196
                MSG_WARNING("Button \"" << button->getButtonName() << "\" deleted because of an error!");
197
                delete button;
198
                return;
199
            }
200
 
201
            button->setHandle(((mPage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
202
            button->createButtons();
203
            addButton(button);
204
            index++;        // Jump over the end tag of the button.
205
        }
206
        else if (e.compare("sr") == 0)
207
        {
208
            SR_T bsr;
209
            bsr.number = xml.getAttributeInt("number", attrs);
210
            index++;
211
 
212
            while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
213
            {
214
                if (ename.compare("bs") == 0)
215
                    bsr.bs = content;
216
                else if (ename.compare("cb") == 0)
217
                    bsr.cb = content;
218
                else if (ename.compare("cf") == 0)
219
                    bsr.cf = content;
220
                else if (ename.compare("ct") == 0)
221
                    bsr.ct = content;
222
                else if (ename.compare("ec") == 0)
223
                    bsr.ec = content;
224
                else if (ename.compare("bm") == 0)
225
                    bsr.bm = content;
226
                else if (ename.compare("mi") == 0)
227
                    bsr.mi = content;
228
                else if (ename.compare("fi") == 0)
229
                    bsr.fi = xml.convertElementToInt(content);
230
                else if (ename.compare("te") == 0)
231
                    bsr.te = content;
232
                else if (ename.compare("tx") == 0)
233
                    bsr.tx = xml.convertElementToInt(content);
234
                else if (ename.compare("ty") == 0)
235
                    bsr.ty = xml.convertElementToInt(content);
236
                else if (ename.compare("et") == 0)
237
                    bsr.et = xml.convertElementToInt(content);
238
                else if (ename.compare("ww") == 0)
239
                    bsr.ww = xml.convertElementToInt(content);
240
                else if (ename.compare("jt") == 0)
241
                    bsr.jt = (Button::TEXT_ORIENTATION)xml.convertElementToInt(content);
242
                else if (ename.compare("jb") == 0)
243
                    bsr.jb = (Button::TEXT_ORIENTATION)xml.convertElementToInt(content);
244
 
245
                oldIndex = index;
246
            }
247
 
248
            sr.push_back(bsr);
249
 
250
            if (index == TExpat::npos)
251
                index = oldIndex + 1;
252
        }
253
    }
254
 
255
    setSR(sr);
256
 
257
    if (TPageInterface::getButtons())
258
        sortButtons();
259
}
260
 
261
void TPage::addProgress()
262
{
263
    DECL_TRACER("TPage::addProgress()");
264
 
265
    if (!gPageManager)
266
    {
267
        MSG_WARNING("The page manager is still not initialized!");
268
        return;
269
    }
270
 
271
    Button::SR_T bsr;
272
    mPage.pageID = 300;
273
    mPage.name = "_progress";
274
    mPage.width = gPageManager->getSettings()->getWidth();
275
    mPage.height = gPageManager->getSettings()->getHeight();
276
    double unit = (double)mPage.height / 10.0;
277
    MSG_DEBUG("One unit is " << unit);
278
    // Background of page
279
    bsr.number = 1;
280
    bsr.cf = "#106010ff";
281
    bsr.ct = "#ffffffff";
282
    bsr.cb = "#009000ff";
283
    bsr.ec = "#ffffffff";
284
    bsr.fi = 21;
285
    sr.push_back(bsr);
286
    // Text field 1 to show status messages
287
    Button::EXTBUTTON_t bt;
288
    bt.type = GENERAL;
289
    bt.bi = 1;
290
    bt.na = "Line1";
291
    bt.tp = (int)(unit * 2.0);
292
    bt.lt = (int)(((double)mPage.width - ((double)mPage.width / 100.0 * 80.0)) / 2.0);
293
    bt.wt = (int)((double)mPage.width / 100.0 * 80.0);    // Line take 80% of available width
294
    bt.ht = (int)(unit / 100.0 * 80.0);
295
    MSG_DEBUG("Dimensions button 1: lt: " << bt.lt << ", tp: " << bt.tp << ", wt: " << bt.wt << ", ht: " << bt.ht);
296
    bt.zo = 1;
297
    bt.ap = 0;
298
    bt.ad = 160;
299
    bsr.cf = "#000000ff";
300
    bt.sr.push_back(bsr);
301
    bsr.number = 2;
302
    bt.sr.push_back(bsr);
303
    TButton *button = new TButton();
304
    button->setPalette(mPalette);
305
    button->setFonts(getFonts());
306
    button->registerCallback(_displayButton);
307
    button->regCallPlayVideo(_playVideo);
308
    button->createSoftButton(bt);
309
    button->setParentSize(mPage.width, mPage.height);
310
 
311
    if (TError::isError())
312
    {
313
        MSG_WARNING("Button \"" << button->getButtonName() << "\" deleted because of an error!");
314
        delete button;
315
        return;
316
    }
317
 
318
    button->setHandle(((mPage.pageID << 16) & 0xffff0000) | bt.bi);
319
    button->createButtons();
320
    addButton(button);
321
    // Text field 2 to show status messages
322
    bt.bi = 2;
323
    bt.na = "Line2";
324
    bt.tp = (int)(unit * 7.0);
325
    MSG_DEBUG("Dimensions button 2: lt: " << bt.lt << ", tp: " << bt.tp << ", wt: " << bt.wt << ", ht: " << bt.ht);
326
    bt.zo = 2;
327
    bt.ad = 161;
328
    button = new TButton();
329
    button->setPalette(mPalette);
330
    button->setFonts(getFonts());
331
    button->registerCallback(_displayButton);
332
    button->regCallPlayVideo(_playVideo);
333
    button->createSoftButton(bt);
334
    button->setParentSize(mPage.width, mPage.height);
335
 
336
    if (TError::isError())
337
    {
338
        MSG_WARNING("Button \"" << button->getButtonName() << "\" deleted because of an error!");
339
        delete button;
340
        return;
341
    }
342
 
343
    button->setHandle(((mPage.pageID << 16) & 0xffff0000) | bt.bi);
344
    button->createButtons();
345
    addButton(button);
346
    // Progress bar 1 (overall status)
347
    bt.type = BARGRAPH;
348
    bt.bi = 3;
349
    bt.na = "Bar1";
350
    bt.tp = (int)(unit * 3.0);
351
    bt.lt = (int)(((double)mPage.width - ((double)mPage.width / 100.0 * 80.0)) / 2.0);
352
    bt.wt = (int)((double)mPage.width / 100.0 * 80.0);    // Line take 80% of available width
353
    bt.ht = (int)unit;
354
    MSG_DEBUG("Dimensions bargraph 1: lt: " << bt.lt << ", tp: " << bt.tp << ", wt: " << bt.wt << ", ht: " << bt.ht);
355
    bt.zo = 3;
356
    bt.ap = 0;
357
    bt.ad = 162;
358
    bt.lp = 0;
359
    bt.lv = 162;
360
    bt.rl = 1;
361
    bt.rh = 100;
362
    bt.sc = "#ffffffff";
363
    bt.dr = "horizontal";
364
    bsr.number = 1;
365
    bsr.cf = "#0e0e0eff";
366
    bsr.ct = "#ffffffff";
367
    bsr.cb = "#009000ff";
368
    bt.sr.clear();
369
    bt.sr.push_back(bsr);
370
    bsr.number = 2;
371
    bsr.cf = "#ffffffff";
372
    bt.sr.push_back(bsr);
373
    button = new TButton();
374
    button->setPalette(mPalette);
375
    button->setFonts(getFonts());
376
    button->registerCallback(_displayButton);
377
    button->regCallPlayVideo(_playVideo);
378
    button->createSoftButton(bt);
379
    button->setParentSize(mPage.width, mPage.height);
380
 
381
    if (TError::isError())
382
    {
383
        MSG_WARNING("Button \"" << button->getButtonName() << "\" deleted because of an error!");
384
        delete button;
385
        return;
386
    }
387
 
388
    button->setHandle(((mPage.pageID << 16) & 0xffff0000) | bt.bi);
389
    button->createButtons();
390
    addButton(button);
391
    // Progress bar 2 (details)
392
    bt.bi = 4;
393
    bt.na = "Bar2";
394
    bt.tp = (int)(unit * 5.0);
395
    MSG_DEBUG("Dimensions bargraph 2: lt: " << bt.lt << ", tp: " << bt.tp << ", wt: " << bt.wt << ", ht: " << bt.ht);
396
    bt.zo = 4;
397
    bt.ad = 163;
398
    bt.lv = 163;
399
    button = new TButton();
400
    button->setPalette(mPalette);
401
    button->setFonts(getFonts());
402
    button->registerCallback(_displayButton);
403
    button->regCallPlayVideo(_playVideo);
404
    button->createSoftButton(bt);
405
    button->setParentSize(mPage.width, mPage.height);
406
 
407
    if (TError::isError())
408
    {
409
        MSG_WARNING("Button \"" << button->getButtonName() << "\" deleted because of an error!");
410
        delete button;
411
        return;
412
    }
413
 
414
    button->setHandle(((mPage.pageID << 16) & 0xffff0000) | bt.bi);
415
    button->createButtons();
416
    addButton(button);
417
}
418
 
419
SkBitmap& TPage::getBgImage()
420
{
421
    DECL_TRACER("TPage::getBgImage()");
422
 
423
    if (!mBgImage.empty())
424
        return mBgImage;
425
 
426
    bool haveImage = false;
427
    MSG_DEBUG("Creating image for page " << mPage.pageID << ": " << mPage.name);
428
    SkBitmap target;
429
 
430
    if (!allocPixels(mPage.width, mPage.height, &target))
431
        return mBgImage;
432
 
433
    target.eraseColor(TColor::getSkiaColor(mPage.sr[0].cf));
434
    // Draw the background, if any
435
    if (mPage.sr.size() > 0 && (!mPage.sr[0].bm.empty() || !mPage.sr[0].mi.empty()))
436
    {
437
        TDrawImage dImage;
438
        dImage.setWidth(mPage.width);
439
        dImage.setHeight(mPage.height);
440
 
441
        if (!mPage.sr[0].bm.empty())
442
        {
443
            MSG_DEBUG("Loading image " << mPage.sr[0].bm);
444
            sk_sp<SkData> rawImage = readImage(mPage.sr[0].bm);
445
            SkBitmap bm;
446
 
447
            if (rawImage)
448
            {
449
                MSG_DEBUG("Decoding image BM ...");
450
 
451
                if (!DecodeDataToBitmap(rawImage, &bm))
452
                {
453
                    MSG_WARNING("Problem while decoding image " << mPage.sr[0].bm);
454
                }
455
                else if (!bm.empty())
456
                {
457
                    dImage.setImageBm(bm);
458
                    SkImageInfo info = bm.info();
459
                    mPage.sr[0].bm_width = info.width();
460
                    mPage.sr[0].bm_height = info.height();
461
                    haveImage = true;
462
                }
463
                else
464
                {
465
                    MSG_WARNING("BM image " << mPage.sr[0].bm << " seems to be empty!");
466
                }
467
            }
468
        }
469
 
470
        if (!mPage.sr[0].mi.empty())
471
        {
472
            MSG_DEBUG("Loading image " << mPage.sr[0].mi);
473
            sk_sp<SkData> rawImage = readImage(mPage.sr[0].mi);
474
            SkBitmap mi;
475
 
476
            if (rawImage)
477
            {
478
                MSG_DEBUG("Decoding image MI ...");
479
 
480
                if (!DecodeDataToBitmap(rawImage, &mi))
481
                {
482
                    MSG_WARNING("Problem while decoding image " << mPage.sr[0].mi);
483
                }
484
                else if (!mi.empty())
485
                {
486
                    dImage.setImageMi(mi);
487
                    SkImageInfo info = mi.info();
488
                    mPage.sr[0].mi_width = info.width();
489
                    mPage.sr[0].mi_height = info.height();
490
                    haveImage = true;
491
                }
492
                else
493
                {
494
                    MSG_WARNING("MI image " << mPage.sr[0].mi << " seems to be empty!");
495
                }
496
            }
497
        }
498
 
499
        if (haveImage)
500
        {
501
            dImage.setSr(mPage.sr);
502
 
503
            if (!dImage.drawImage(&target))
504
                return mBgImage;
505
 
506
            if (!mPage.sr[0].te.empty())
507
            {
508
                if (!drawText(mPage, &target))
509
                    return mBgImage;
510
            }
511
 
512
            if (mPage.sr[0].oo < 255 && mPage.sr[0].te.empty() && mPage.sr[0].bs.empty())
513
                setOpacity(&target, mPage.sr[0].oo);
514
 
515
#ifdef _SCALE_SKIA_
516
            size_t rowBytes = info.minRowBytes();
517
            size_t size = info.computeByteSize(rowBytes);
518
            SkImageInfo info = target.info();
519
 
520
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
521
            {
522
                SkPaint paint;
523
                int left, top;
524
 
525
                paint.setBlendMode(SkBlendMode::kSrc);
526
                paint.setFilterQuality(kHigh_SkFilterQuality);
527
                // Calculate new dimension
528
                double scaleFactor = gPageManager->getScaleFactor();
529
                MSG_DEBUG("Using scale factor " << scaleFactor);
530
                int lwidth = (int)((double)info.width() * scaleFactor);
531
                int lheight = (int)((double)info.height() * scaleFactor);
532
                int twidth = (int)((double)mPage.width * scaleFactor);
533
                int theight = (int)((double)mPage.height * scaleFactor);
534
                calcPosition(lwidth, lheight, &left, &top);
535
                // Create a canvas and draw new image
536
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
537
 
538
                if (!allocPixels(twidth, theight, &target))
539
                    return;
540
 
541
                target.eraseColor(TColor::getSkiaColor(mPage.sr[0].cf));
542
                SkCanvas can(target, SkSurfaceProps());
543
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
544
                can.drawImageRect(im, rect, &paint);
545
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
546
            }
547
#endif
548
        }
549
    }
550
 
551
    if (mPage.sr.size() > 0 && !mPage.sr[0].te.empty())
552
    {
553
        MSG_DEBUG("Drawing a text only on background image ...");
554
 
555
        if (drawText(mPage, &target))
556
            haveImage = true;
557
    }
558
 
559
    // Check for a frame and draw it if there is one.
560
    if (!mPage.sr[0].bs.empty())
561
    {
562
        if (drawFrame(mPage, &target))
563
            haveImage = true;
564
    }
565
 
566
    if (haveImage)
567
    {
568
        if (mPage.sr[0].oo < 255)
569
            setOpacity(&target, mPage.sr[0].oo);
570
 
571
        mBgImage = target;
572
    }
573
 
574
    return mBgImage;
575
}
576
 
577
void TPage::show()
578
{
579
    DECL_TRACER("TPage::show()");
580
 
581
    if (!_setBackground)
582
    {
583
        if (gPageManager && gPageManager->getCallbackBG())
584
            _setBackground = gPageManager->getCallbackBG();
585
        else
586
        {
587
            MSG_WARNING("No callback \"setBackground\" was set!");
588
#if TESTMODE == 1
589
            setScreenDone();
590
#endif
591
            return;
592
        }
593
    }
594
 
595
    bool haveImage = false;
596
    ulong handle = (mPage.pageID << 16) & 0xffff0000;
597
    MSG_DEBUG("Processing page " << mPage.pageID);
598
    SkBitmap target;
599
 
600
    if (!allocPixels(mPage.width, mPage.height, &target))
601
    {
602
#if TESTMODE == 1
603
        setScreenDone();
604
#endif
605
        return;
606
    }
607
 
608
    target.eraseColor(TColor::getSkiaColor(sr[0].cf));
609
    // Draw the background, if any
610
    if (sr.size() > 0 && (!sr[0].bm.empty() || !sr[0].mi.empty()))
611
    {
612
        TDrawImage dImage;
613
        dImage.setWidth(mPage.width);
614
        dImage.setHeight(mPage.height);
615
 
616
        if (!sr[0].bm.empty())
617
        {
618
            MSG_DEBUG("Loading image " << sr[0].bm);
619
            sk_sp<SkData> rawImage = readImage(sr[0].bm);
620
            SkBitmap bm;
621
 
622
            if (rawImage && !rawImage->isEmpty())
623
            {
624
                MSG_DEBUG("Decoding image BM ...");
625
 
626
                if (!DecodeDataToBitmap(rawImage, &bm))
627
                {
628
                    MSG_WARNING("Problem while decoding image " << sr[0].bm);
629
                }
630
                else if (!bm.isNull() && !bm.empty())
631
                {
632
                    dImage.setImageBm(bm);
633
                    SkImageInfo info = bm.info();
634
                    sr[0].bm_width = info.width();
635
                    sr[0].bm_height = info.height();
636
                    haveImage = true;
637
                    MSG_DEBUG("Image " << sr[0].bm << " has dimension " << sr[0].bm_width << " x " << sr[0].bm_height);
638
                }
639
                else
640
                {
641
                    MSG_WARNING("BM image " << sr[0].bm << " seems to be empty!");
642
                }
643
            }
644
        }
645
 
646
        MSG_DEBUG("haveImage: " << (haveImage ? "TRUE" : "FALSE"));
647
 
648
        if (!sr[0].mi.empty())
649
        {
650
            MSG_DEBUG("Loading image " << sr[0].mi);
651
            sk_sp<SkData> rawImage = readImage(sr[0].mi);
652
            SkBitmap mi;
653
 
654
            if (rawImage && !rawImage->isEmpty())
655
            {
656
                MSG_DEBUG("Decoding image MI ...");
657
 
658
                if (!DecodeDataToBitmap(rawImage, &mi))
659
                {
660
                    MSG_WARNING("Problem while decoding image " << sr[0].mi);
661
                }
662
                else if (!mi.isNull() && !mi.empty())
663
                {
664
                    dImage.setImageMi(mi);
665
                    SkImageInfo info = mi.info();
666
                    sr[0].mi_width = info.width();
667
                    sr[0].mi_height = info.height();
668
                    haveImage = true;
669
                }
670
                else
671
                {
672
                    MSG_WARNING("MI image " << sr[0].mi << " seems to be empty!");
673
                }
674
            }
675
        }
676
 
677
        if (haveImage)
678
        {
679
            dImage.setSr(sr);
680
 
681
            if (!dImage.drawImage(&target))
682
            {
683
#if TESTMODE == 1
684
                setScreenDone();
685
#endif
686
                return;
687
            }
688
 
689
            if (!sr[0].te.empty())
690
            {
691
                if (!drawText(mPage, &target))
692
                {
693
#if TESTMODE == 1
694
                    setScreenDone();
695
#endif
696
                    return;
697
                }
698
            }
699
 
700
#ifdef _SCALE_SKIA_
701
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
702
            {
703
                SkPaint paint;
704
                int left, top;
705
                SkImageInfo info = target.info();
706
 
707
                paint.setBlendMode(SkBlendMode::kSrc);
708
                paint.setFilterQuality(kHigh_SkFilterQuality);
709
                // Calculate new dimension
710
                double scaleFactor = gPageManager->getScaleFactor();
711
                MSG_DEBUG("Using scale factor " << scaleFactor);
712
                int lwidth = (int)((double)info.width() * scaleFactor);
713
                int lheight = (int)((double)info.height() * scaleFactor);
714
                int twidth = (int)((double)width * scaleFactor);
715
                int theight = (int)((double)height * scaleFactor);
716
                calcPosition(lwidth, lheight, &left, &top);
717
                // Create a canvas and draw new image
718
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
719
 
720
                if (!allocPixels(twidth, theight, &target))
721
                {
722
#if TESTMODE == 1
723
                    setScreenDone();
724
#endif
725
                    return;
726
                }
727
 
728
                target.eraseColor(TColor::getSkiaColor(sr[0].cf));
729
                SkCanvas can(target, SkSurfaceProps());
730
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
731
                can.drawImageRect(im, rect, &paint);
732
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
733
            }
734
#endif
735
/*
736
            TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
737
#ifdef _OPAQUE_SKIA_
738
            if (sr[0].te.empty() && sr[0].bs.empty())
739
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(sr[0].cf));
740
#else
741
            if (sr[0].te.empty() && sr[0].bs.empty())
742
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(sr[0].cf), sr[0].oo);
743
#endif
744
*/
745
        }
746
    }
747
 
748
    if (sr.size() > 0 && !sr[0].te.empty())
749
    {
750
        MSG_DEBUG("Drawing text on background image ...");
751
 
752
        if (drawText(mPage, &target))
753
            haveImage = true;
754
    }
755
 
756
    // Check for a frame and draw it if there is one.
757
    if (!sr[0].bs.empty())
758
    {
759
        if (drawFrame(mPage, &target))
760
            haveImage = true;
761
    }
762
 
763
    if (haveImage)
764
    {
765
        SkImageInfo info = target.info();
766
        TBitmap image((unsigned char *)target.getPixels(), info.width(), info.height());
767
#ifdef _OPAQUE_SKIA_
768
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(sr[0].cf));
769
#else
770
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(sr[0].cf), sr[0].oo);
771
#endif
772
    }
773
    else if (sr.size() > 0 && !haveImage)
774
    {
775
        MSG_DEBUG("Calling \"setBackground\" with no image ...");
776
#ifdef _OPAQUE_SKIA_
777
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(sr[0].cf));
778
#else
779
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(sr[0].cf), sr[0].oo);
780
#endif
781
    }
782
 
783
    // Draw the buttons
784
    BUTTONS_T *button = TPageInterface::getButtons();
785
 
786
    while (button)
787
    {
788
        if (button->button)
789
        {
790
            MSG_DEBUG("Drawing button " << button->button->getButtonIndex() << ": " << button->button->getButtonName());
791
            button->button->registerCallback(_displayButton);
792
            button->button->regCallPlayVideo(_playVideo);
793
            TPageInterface::registerListCallback<TPage>(button->button, this);
794
            button->button->setFonts(getFonts());
795
            button->button->setPalette(mPalette);
796
            button->button->createButtons();
797
 
798
            if (sr.size() > 0)
799
                button->button->setGlobalOpacity(sr[0].oo);
800
 
801
            button->button->show();
802
        }
803
 
804
        button = button->next;
805
    }
806
 
807
    // Mark page as visible
808
    mVisible = true;
809
 
810
    if (gPageManager && gPageManager->getPageFinished())
811
        gPageManager->getPageFinished()(handle);
812
}
813
 
814
bool TPage::addSubPage(TSubPage* pg)
815
{
816
    DECL_TRACER("TPage::addSubPage(TSubPage* pg)");
817
 
818
    if (!pg)
819
    {
820
        MSG_ERROR("Parameter is NULL!");
821
        TError::setError();
822
        return false;
823
    }
824
 
825
    if (mSubPages.empty())
826
        mZOrder = 0;
827
 
828
#if __cplusplus < 201703L
829
    map<int, TSubPage *>::iterator iter = mSubPages.find(pg->getNumber());
830
 
831
    if (iter != mSubPages.end() && iter->second != pg)
832
        iter->second = pg;
833
    else
834
        mSubPages.insert(pair<int, TSubPage *>(pg->getNumber(), pg));
835
#else
836
    mSubPages.insert_or_assign(pg->getNumber(), pg);
837
#endif
838
    mLastSubPage = 0;
839
    return true;
840
}
841
 
842
bool TPage::removeSubPage(int ID)
843
{
844
    DECL_TRACER("TPage::removeSubPage(int ID)");
845
 
846
    map<int, TSubPage *>::iterator iter = mSubPages.find(ID);
847
 
848
    if (iter != mSubPages.end())
849
    {
850
        mSubPages.erase(iter);
851
        return true;
852
    }
853
 
854
    return false;
855
}
856
 
857
bool TPage::removeSubPage(const std::string& nm)
858
{
859
    DECL_TRACER("TPage::removeSubPage(const std::string& nm)");
860
 
861
    if (mSubPages.empty())
862
        return false;
863
 
864
    map<int, TSubPage *>::iterator iter;
865
 
866
    for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
867
    {
868
        if (iter->second->getName() == nm)
869
        {
870
            mSubPages.erase(iter);
871
            return true;
872
        }
873
    }
874
 
875
    return false;
876
}
877
 
878
TSubPage *TPage::getSubPage(int pageID)
879
{
880
    DECL_TRACER("TPage::getSubPage(int pageID)");
881
 
882
    map<int, TSubPage *>::iterator iter = mSubPages.find(pageID);
883
 
884
    if (iter != mSubPages.end())
885
        return iter->second;
886
 
887
    mLastSubPage = 0;
888
    return nullptr;
889
}
890
 
891
TSubPage *TPage::getSubPage(const std::string& name)
892
{
893
    DECL_TRACER("TPage::getSubPage(const std::string& name)");
894
 
895
    if (mSubPages.empty())
896
        return nullptr;
897
 
898
    map<int, TSubPage *>::iterator iter;
899
 
900
    for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
901
    {
902
        if (iter->second->getName() == name)
903
            return iter->second;
904
    }
905
 
906
    mLastSubPage = 0;
907
    return nullptr;
908
}
909
 
910
TSubPage *TPage::getFirstSubPage()
911
{
912
    DECL_TRACER("TPage::getFirstSubPage()");
913
 
914
    if (mSubPages.empty())
915
    {
916
        MSG_DEBUG("No subpages in chain.");
917
        mLastSubPage = 0;
918
        return nullptr;
919
    }
920
 
921
    TSubPage *pg = mSubPages.begin()->second;
922
 
923
    if (!pg)
924
    {
925
        MSG_ERROR("The pointer to the subpage " << mSubPages.begin()->first << " is NULL!");
926
        return nullptr;
927
    }
928
 
929
    mLastSubPage = pg->getNumber();
930
    MSG_DEBUG("Subpage (Z: " << pg->getZOrder() << "): " << pg->getNumber() << ". " << pg->getName());
931
    return pg;
932
}
933
 
934
TSubPage *TPage::getNextSubPage()
935
{
936
    DECL_TRACER("TPage::getNextSubPage()");
937
 
938
    if (mSubPages.empty())
939
    {
940
        MSG_DEBUG("No subpages in chain.");
941
        mLastSubPage = 0;
942
        return nullptr;
943
    }
944
 
945
    if (mLastSubPage <= 0)
946
        mLastSubPage = mSubPages.begin()->second->getNumber();
947
 
948
    map<int, TSubPage *>::iterator iter = mSubPages.find(mLastSubPage);
949
 
950
    if (iter != mSubPages.end())
951
        iter++;
952
    else
953
    {
954
        MSG_DEBUG("No more subpages in chain.");
955
        mLastSubPage = 0;
956
        return nullptr;
957
    }
958
 
959
    if (iter != mSubPages.end())
960
    {
961
        TSubPage *page = iter->second;
962
        mLastSubPage = page->getNumber();
963
        MSG_DEBUG("Subpage (Z: " << page->getZOrder() << "): " << page->getNumber() << ". " << page->getName());
964
        return page;
965
    }
966
 
967
    MSG_DEBUG("No more subpages in chain.");
968
    mLastSubPage = 0;
969
    return nullptr;
970
}
971
 
972
TSubPage *TPage::getPrevSubPage()
973
{
974
    DECL_TRACER("TPage::getPrevSubPage()");
975
 
976
    if (mSubPages.empty())
977
    {
978
        MSG_DEBUG("No last subpage or no subpages at all!");
979
        mLastSubPage = 0;
980
        return nullptr;
981
    }
982
 
983
    if (mLastSubPage < MAX_PAGE_ID)
984
    {
985
        map<int, TSubPage *>::iterator iter = mSubPages.end();
986
        iter--;
987
        mLastSubPage = iter->first;
988
    }
989
 
990
    map<int, TSubPage *>::iterator iter = mSubPages.find(mLastSubPage);
991
 
992
    if (iter != mSubPages.end() && iter != mSubPages.begin())
993
        iter--;
994
    else
995
    {
996
        MSG_DEBUG("No more subpages in chain.");
997
        mLastSubPage = 0;
998
        return nullptr;
999
    }
1000
 
1001
    TSubPage *page = iter->second;
1002
    mLastSubPage = page->getNumber();
1003
    MSG_DEBUG("Subpage (Z: " << page->getZOrder() << "): " << page->getNumber() << ". " << page->getName());
1004
    return page;
1005
}
1006
 
1007
TSubPage *TPage::getLastSubPage()
1008
{
1009
    DECL_TRACER("TPage::getLastSubPage()");
1010
 
1011
    if (mSubPages.empty())
1012
    {
1013
        mLastSubPage = 0;
1014
        MSG_DEBUG("No subpages in cache!");
1015
        return nullptr;
1016
    }
1017
 
1018
    map<int, TSubPage *>::iterator iter = mSubPages.end();
1019
    iter--;
1020
    TSubPage *pg = iter->second;
1021
    mLastSubPage = pg->getNumber();
1022
    MSG_DEBUG("Subpage (Z: " << pg->getZOrder() << "): " << pg->getNumber() << ". " << pg->getName());
1023
    return pg;
1024
}
1025
 
1026
void TPage::drop()
1027
{
1028
    DECL_TRACER("TPage::drop()");
1029
 
1030
    // remove all subpages, if there are any
1031
#if TESTMODE == 1
1032
    _block_screen = true;
1033
#endif
1034
    if (!mSubPages.empty())
1035
    {
1036
        map<int, TSubPage *>::iterator iter;
1037
 
1038
        for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
1039
        {
1040
            if (iter->second)
1041
                iter->second->drop();
1042
        }
1043
    }
1044
#if TESTMODE == 1
1045
    _block_screen = false;
1046
#endif
1047
 
1048
    // remove all buttons, if there are any
1049
    BUTTONS_T *bt = TPageInterface::getButtons();
1050
 
1051
    while (bt)
1052
    {
1053
        MSG_DEBUG("Dropping button " << handleToString(bt->button->getHandle()));
1054
        bt->button->invalidate();
1055
        bt = bt->next;
1056
    }
1057
 
1058
    if (gPageManager && gPageManager->getCallDropPage())
1059
    {
1060
        ulong handle = (mPage.pageID << 16) & 0xffff0000;
1061
        gPageManager->getCallDropPage()(handle);
1062
    }
1063
 
1064
    mZOrder = ZORDER_INVALID;
1065
    mVisible = false;
1066
}
1067
#ifdef _SCALE_SKIA_
1068
void TPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)
1069
#else
1070
void TPage::calcPosition(int im_width, int im_height, int *left, int *top)
1071
#endif
1072
{
1073
    DECL_TRACER("TPage::calcPosition(int im_width, int im_height, int *left, int *top)");
1074
 
1075
    int nw = mPage.width;
1076
    int nh = mPage.height;
1077
#ifdef _SCALE_SKIA_
1078
    if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
1079
    {
1080
        nw = (int)((double)width * gPageManager->getScaleFactor());
1081
        nh = (int)((double)height * gPageManager->getScaleFactor());
1082
    }
1083
#endif
1084
    switch (sr[0].jb)
1085
    {
1086
        case 0: // absolute position
1087
            *left = sr[0].bx;
1088
            *top = sr[0].by;
1089
#ifdef _SCALE_SKIA_
1090
            if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
1091
            {
1092
                *left = (int)((double)sr[0].bx * gPageManager->getScaleFactor());
1093
                *left = (int)((double)sr[0].by * gPageManager->getScaleFactor());
1094
            }
1095
#endif
1096
        break;
1097
 
1098
        case 1: // top, left
1099
            *left = 0;
1100
            *top = 0;
1101
        break;
1102
 
1103
        case 2: // center, top
1104
            *left = (nw - im_width) / 2;
1105
            *top = 0;
1106
        break;
1107
 
1108
        case 3: // right, top
1109
            *left = nw - im_width;
1110
            *top = 0;
1111
        break;
1112
 
1113
        case 4: // left, middle
1114
            *left = 0;
1115
            *top = (nh - im_height) / 2;
1116
        break;
1117
 
1118
        case 6: // right, middle
1119
            *left = nw - im_width;
1120
            *top = (nh - im_height) / 2;
1121
        break;
1122
 
1123
        case 7: // left, bottom
1124
            *left = 0;
1125
            *top = nh - im_height;
1126
        break;
1127
 
1128
        case 8: // center, bottom
1129
            *left = (nw - im_width) / 2;
1130
            *top = nh - im_height;
1131
        break;
1132
 
1133
        case 9: // right, bottom
1134
            *left = nw - im_width;
1135
            *top = nh - im_height;
1136
        break;
1137
 
1138
        default:    // center middle
1139
            *left = (nw - im_width) / 2;
1140
            *top = (nh - im_height) / 2;
1141
    }
1142
 
1143
    if (*left < 0)
1144
        *left = 0;
1145
 
1146
    if (*top < 0)
1147
        *top = 0;
1148
}
1149
 
1150
void TPage::sortSubpages()
1151
{
1152
    DECL_TRACER("TPage::sortSubpage()");
1153
 
1154
    mSubPagesSorted.clear();
1155
 
1156
    if (mSubPages.empty())
1157
        return;
1158
 
1159
    map<int, TSubPage *>::iterator iter;
1160
 
1161
    for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
1162
    {
1163
        if (iter->second->getZOrder() >= 0)
1164
        {
1165
            mSubPagesSorted.insert(pair<int, TSubPage *>(iter->second->getZOrder(), iter->second));
1166
            MSG_DEBUG("Page " << iter->second->getNumber() << " (" << iter->second->getName() << "): sorted in with Z-Order " << iter->second->getZOrder());
1167
        }
1168
    }
1169
}
1170
 
1171
map<int, TSubPage *>& TPage::getSortedSubpages(bool force)
1172
{
1173
    DECL_TRACER("TPage::getSortedSubpages(bool force)");
1174
 
1175
    if (mSubPagesSorted.empty() || force)
1176
        sortSubpages();
1177
 
1178
    return mSubPagesSorted;
1179
}
1180
 
1181
int TPage::getNextZOrder()
1182
{
1183
    DECL_TRACER("TPage::getNextZOrder()");
1184
 
1185
    // Find highest z-order number
1186
    int cnt = 0;
1187
    map<int, TSubPage *>::iterator iter;
1188
 
1189
    for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
1190
    {
1191
        int zo = iter->second->getZOrder();
1192
 
1193
        if (iter->second && zo != ZORDER_INVALID)
1194
        {
1195
            if (cnt < zo)
1196
                cnt = zo;
1197
        }
1198
    }
1199
 
1200
    mZOrder = cnt + 1;
1201
    MSG_DEBUG("New Z-order: " << mZOrder);
1202
    return mZOrder;
1203
}
1204
 
1205
int TPage::decZOrder()
1206
{
1207
    DECL_TRACER("TPage::decZOrder()");
1208
 
1209
    // Find highest z-order number
1210
    int cnt = 0;
1211
    map<int, TSubPage *>::iterator iter;
1212
 
1213
    for (iter = mSubPages.begin(); iter != mSubPages.end(); ++iter)
1214
    {
1215
        int zo = iter->second->getZOrder();
1216
 
1217
        if (iter->second && zo != ZORDER_INVALID)
1218
        {
1219
            if (cnt < zo)
1220
                cnt = zo;
1221
        }
1222
    }
1223
 
1224
    mZOrder = cnt;
1225
    return mZOrder;
1226
}