Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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