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