Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 andreas 1
/*
21 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
3 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
5 andreas 19
#include <exception>
20
 
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
 
33
using std::string;
6 andreas 34
using std::vector;
3 andreas 35
using namespace Button;
76 andreas 36
using namespace Expat;
3 andreas 37
 
26 andreas 38
extern TPageManager *gPageManager;
39
 
3 andreas 40
TSubPage::TSubPage(const string& name)
41
    : mFile(name)
42
{
43
    DECL_TRACER("TSubPage::TSubPage(const string& path)");
14 andreas 44
    TError::clear();
3 andreas 45
    string path = makeFileName(TConfig::getProjectPath(), name);
46
 
47
    if (isValidFile())
48
        mFName = getFileName();
49
    else
50
    {
51
        MSG_ERROR("Either the path \"" << TConfig::getProjectPath() << "\" or the file name \"" << name << "\" is invalid!");
52
        TError::setError();
53
        return;
54
    }
55
 
38 andreas 56
    if (gPageManager)
57
    {
58
        if (!_displayButton)
59
            _displayButton = gPageManager->getCallbackDB();
60
 
61
        if (!_setBackground)
62
            _setBackground = gPageManager->getCallbackBG();
63
 
64
        if (!_playVideo)
65
            _playVideo = gPageManager->getCallbackPV();
66
 
67
        if (!_callDropSubPage)
68
            _callDropSubPage = gPageManager->getCallDropSubPage();
69
    }
70
 
3 andreas 71
    initialize();
72
}
73
 
74
TSubPage::~TSubPage()
75
{
76
    DECL_TRACER("TSubPage::~TSubPage()");
77
 
5 andreas 78
    if (mSubpage.name.empty())
79
    {
80
        MSG_WARNING("Invalid page found!");
81
        return;
82
    }
83
 
84
    MSG_DEBUG("Destroing subpage " << mSubpage.pageID << ": " << mSubpage.name);
85
 
3 andreas 86
    BUTTONS_T *b = mButtons;
87
    BUTTONS_T *next = nullptr;
88
 
89
    while (b)
90
    {
91
        next = b->next;
5 andreas 92
 
93
        if (b->button)
94
            delete b->button;
95
 
3 andreas 96
        delete b;
97
        b = next;
98
    }
99
 
100
    mButtons = nullptr;
101
}
102
 
103
void TSubPage::initialize()
104
{
105
    DECL_TRACER("TSubPage::initialize()");
106
 
107
    if (mFName.empty())
108
        return;
109
 
110
    TError::clear();
76 andreas 111
    TExpat xml(mFName);
112
    xml.setEncoding(ENC_CP1250);
3 andreas 113
 
76 andreas 114
    if (!xml.parse())
3 andreas 115
        return;
116
 
76 andreas 117
    int depth = 0;
118
    size_t index = 0;
119
    size_t oldIndex = 0;
120
    string ename, content;
121
    vector<ATTRIBUTE_t> attrs;
3 andreas 122
 
76 andreas 123
    if ((index = xml.getElementIndex("page", &depth)) == TExpat::npos)
3 andreas 124
    {
125
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found! Invalid XML file!");
126
        TError::setError();
127
        return;
128
    }
129
 
76 andreas 130
    attrs = xml.getAttributes();
131
    string stype = xml.getAttribute("type", attrs);
132
 
133
    if (stype.compare("subpage") != 0)
3 andreas 134
    {
76 andreas 135
        MSG_ERROR("The type " << stype << " is invalid for a subpage!");
3 andreas 136
        TError::setError();
137
        return;
138
    }
139
 
76 andreas 140
    mSubpage.popupType = xml.getAttribute("popupType", attrs);  // ??
3 andreas 141
 
76 andreas 142
    while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
3 andreas 143
    {
144
        if (ename.compare("pageID") == 0)
76 andreas 145
            mSubpage.pageID = xml.convertElementToInt(content);
3 andreas 146
        else if (ename.compare("name") == 0)
76 andreas 147
            mSubpage.name = content;
7 andreas 148
        else if (ename.compare("left") == 0)
76 andreas 149
            mSubpage.left = xml.convertElementToInt(content);
7 andreas 150
        else if (ename.compare("top") == 0)
76 andreas 151
            mSubpage.top = xml.convertElementToInt(content);
3 andreas 152
        else if (ename.compare("width") == 0)
76 andreas 153
            mSubpage.width = xml.convertElementToInt(content);
3 andreas 154
        else if (ename.compare("height") == 0)
76 andreas 155
            mSubpage.height = xml.convertElementToInt(content);
3 andreas 156
        else if (ename.compare("group") == 0)
76 andreas 157
            mSubpage.group = content;
7 andreas 158
        else if (ename.compare("showEffect") == 0)
76 andreas 159
            mSubpage.showEffect = (SHOWEFFECT)xml.convertElementToInt(content);
3 andreas 160
        else if (ename.compare("showTime") == 0)
76 andreas 161
            mSubpage.showTime = xml.convertElementToInt(content);
3 andreas 162
        else if (ename.compare("hideTime") == 0)
76 andreas 163
            mSubpage.hideTime = xml.convertElementToInt(content);
7 andreas 164
        else if (ename.compare("hideEffect") == 0)
76 andreas 165
            mSubpage.hideEffect = (SHOWEFFECT)xml.convertElementToInt(content);
3 andreas 166
        else if (ename.compare("timeout") == 0)
76 andreas 167
            mSubpage.timeout = xml.convertElementToInt(content);
3 andreas 168
        else if (ename.compare("button") == 0)      // Read a button
169
        {
170
            try
171
            {
172
                TButton *button = new TButton();
4 andreas 173
                button->setPalette(mPalette);
7 andreas 174
                button->setFonts(mFonts);
76 andreas 175
                index = button->initialize(&xml, index);
6 andreas 176
                button->setParentSize(mSubpage.width, mSubpage.height);
7 andreas 177
                button->registerCallback(_displayButton);
21 andreas 178
                button->regCallPlayVideo(_playVideo);
3 andreas 179
 
180
                if (TError::isError())
181
                {
16 andreas 182
                    MSG_ERROR("Dropping button because of previous errors!");
3 andreas 183
                    delete button;
184
                    return;
185
                }
186
 
5 andreas 187
                button->setHandle(((mSubpage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
14 andreas 188
                button->createButtons();
3 andreas 189
                addButton(button);
76 andreas 190
                index++;    // Jump over the end tag of the button.
3 andreas 191
            }
192
            catch (std::exception& e)
193
            {
194
                MSG_ERROR("Memory exception: " << e.what());
195
                TError::setError();
196
                return;
197
            }
198
        }
6 andreas 199
        else if (ename.compare("sr") == 0)
200
        {
201
            SR_T sr;
76 andreas 202
            sr.number = xml.getAttributeInt("number", attrs);
3 andreas 203
 
76 andreas 204
            while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
6 andreas 205
            {
206
                if (ename.compare("bs") == 0)
76 andreas 207
                    sr.bs = content;
6 andreas 208
                else if (ename.compare("cb") == 0)
76 andreas 209
                    sr.cb = content;
6 andreas 210
                else if (ename.compare("cf") == 0)
76 andreas 211
                    sr.cf = content;
6 andreas 212
                else if (ename.compare("ct") == 0)
76 andreas 213
                    sr.ct = content;
6 andreas 214
                else if (ename.compare("ec") == 0)
76 andreas 215
                    sr.ec = content;
6 andreas 216
                else if (ename.compare("bm") == 0)
76 andreas 217
                    sr.bm = content;
65 andreas 218
                else if (ename.compare("mi") == 0)
76 andreas 219
                    sr.mi = content;
10 andreas 220
                else if (ename.compare("ji") == 0)
76 andreas 221
                    sr.ji = xml.convertElementToInt(content);
10 andreas 222
                else if (ename.compare("jb") == 0)
76 andreas 223
                    sr.jb = xml.convertElementToInt(content);
6 andreas 224
                else if (ename.compare("fi") == 0)
76 andreas 225
                    sr.fi = xml.convertElementToInt(content);
10 andreas 226
                else if (ename.compare("ii") == 0)
76 andreas 227
                    sr.ii = xml.convertElementToInt(content);
10 andreas 228
                else if (ename.compare("ix") == 0)
76 andreas 229
                    sr.ix = xml.convertElementToInt(content);
10 andreas 230
                else if (ename.compare("iy") == 0)
76 andreas 231
                    sr.iy = xml.convertElementToInt(content);
10 andreas 232
                else if (ename.compare("oo") == 0)
76 andreas 233
                    sr.oo = xml.convertElementToInt(content);
54 andreas 234
                else if (ename.compare("te") == 0)
76 andreas 235
                    sr.te = content;
54 andreas 236
                else if (ename.compare("tx") == 0)
76 andreas 237
                    sr.tx = xml.convertElementToInt(content);
54 andreas 238
                else if (ename.compare("ty") == 0)
76 andreas 239
                    sr.ty = xml.convertElementToInt(content);
54 andreas 240
                else if (ename.compare("et") == 0)
76 andreas 241
                    sr.et = xml.convertElementToInt(content);
54 andreas 242
                else if (ename.compare("ww") == 0)
76 andreas 243
                    sr.ww = xml.convertElementToInt(content);
54 andreas 244
                else if (ename.compare("jt") == 0)
76 andreas 245
                    sr.jt = (Button::TEXT_ORIENTATION)xml.convertElementToInt(content);
6 andreas 246
 
76 andreas 247
                oldIndex = index;
6 andreas 248
            }
249
 
250
            mSubpage.sr.push_back(sr);
251
        }
252
 
76 andreas 253
        if (index == TExpat::npos)
254
            index = oldIndex + 1;
3 andreas 255
    }
8 andreas 256
 
76 andreas 257
    if (TStreamError::checkFilter(HLOG_DEBUG))
258
    {
259
        MSG_DEBUG("PageID: " << mSubpage.pageID);
260
        MSG_DEBUG("Name  : " << mSubpage.name);
261
        MSG_DEBUG("Left  : " << mSubpage.left);
262
        MSG_DEBUG("Top   : " << mSubpage.top);
263
        MSG_DEBUG("Width : " << mSubpage.width);
264
        MSG_DEBUG("Height: " << mSubpage.height);
265
 
266
        vector<SR_T>::iterator iter;
267
        size_t pos = 1;
268
 
269
        for (iter = mSubpage.sr.begin(); iter != mSubpage.sr.end(); ++iter)
270
        {
271
            MSG_DEBUG("   " << pos << ": id: " << iter->number);
272
            MSG_DEBUG("   " << pos << ": bs: " << iter->bs);
273
            MSG_DEBUG("   " << pos << ": cb: " << iter->cb);
274
            MSG_DEBUG("   " << pos << ": cf: " << iter->cf);
275
            MSG_DEBUG("   " << pos << ": ct: " << iter->ct);
276
            MSG_DEBUG("   " << pos << ": ec: " << iter->ec);
277
            MSG_DEBUG("   " << pos << ": bm: " << iter->bm);
278
            MSG_DEBUG("   " << pos << ": mi: " << iter->mi);
279
            MSG_DEBUG("   " << pos << ": fi: " << iter->fi);
280
            pos++;
281
        }
282
    }
283
 
8 andreas 284
    // Here the sort function could be called. But it's not necessary because
285
    // the buttons are stored in ascending Z order. Therefor the following
286
    // method call is commented out.
287
    // sortButtons();
6 andreas 288
}
3 andreas 289
 
6 andreas 290
void TSubPage::show()
291
{
292
    DECL_TRACER("TSubPage::show()");
3 andreas 293
 
6 andreas 294
    if (!_setBackground)
3 andreas 295
    {
66 andreas 296
        if (gPageManager && gPageManager->getCallbackBG())
297
            _setBackground = gPageManager->getCallbackBG();
298
        else
299
        {
300
            MSG_WARNING("No callback \"setBackground\" was set!");
301
            return;
302
        }
3 andreas 303
    }
304
 
65 andreas 305
    bool haveImage = false;
6 andreas 306
    ulong handle = (mSubpage.pageID << 16) & 0xffff0000;
53 andreas 307
    MSG_DEBUG("Processing subpage " << mSubpage.pageID << ": " << mSubpage.name);
66 andreas 308
    SkBitmap target;
309
    target.allocN32Pixels(mSubpage.width, mSubpage.height);
310
    target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
6 andreas 311
    // Draw the background, if any
65 andreas 312
    if (mSubpage.sr.size() > 0 && (!mSubpage.sr[0].bm.empty() || !mSubpage.sr[0].mi.empty()))
3 andreas 313
    {
65 andreas 314
        TDrawImage dImage;
66 andreas 315
        dImage.setWidth(mSubpage.width);
316
        dImage.setHeight(mSubpage.height);
3 andreas 317
 
65 andreas 318
        if (!mSubpage.sr[0].bm.empty())
3 andreas 319
        {
65 andreas 320
            MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
321
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
6 andreas 322
            SkBitmap bm;
19 andreas 323
 
65 andreas 324
            if (rawImage)
325
            {
326
                MSG_DEBUG("Decoding image BM ...");
19 andreas 327
 
65 andreas 328
                if (!DecodeDataToBitmap(rawImage, &bm))
329
                {
330
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
331
                }
332
                else if (!bm.empty())
333
                {
334
                    dImage.setImageBm(bm);
335
                    SkImageInfo info = bm.info();
336
                    mSubpage.sr[0].bm_width = info.width();
337
                    mSubpage.sr[0].bm_height = info.height();
338
                    haveImage = true;
339
                }
340
                else
341
                {
342
                    MSG_WARNING("BM image " << mSubpage.sr[0].bm << " seems to be empty!");
343
                }
344
            }
345
        }
54 andreas 346
 
65 andreas 347
        if (!mSubpage.sr[0].mi.empty())
348
        {
349
            MSG_DEBUG("Loading image " << mSubpage.sr[0].mi);
350
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].mi);
351
            SkBitmap mi;
352
 
353
            if (rawImage)
354
            {
355
                MSG_DEBUG("Decoding image MI ...");
356
 
357
                if (!DecodeDataToBitmap(rawImage, &mi))
358
                {
359
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].mi);
360
                }
361
                else if (!mi.empty())
362
                {
363
                    dImage.setImageMi(mi);
364
                    SkImageInfo info = mi.info();
365
                    mSubpage.sr[0].mi_width = info.width();
366
                    mSubpage.sr[0].mi_height = info.height();
367
                    haveImage = true;
368
                }
369
                else
370
                {
371
                    MSG_WARNING("MI image " << mSubpage.sr[0].mi << " seems to be empty!");
372
                }
373
            }
374
        }
375
 
376
        if (haveImage)
377
        {
378
            dImage.setSr(mSubpage.sr);
379
 
380
            if (!dImage.drawImage(&target))
381
                return;
382
 
54 andreas 383
            if (!mSubpage.sr[0].te.empty())
384
            {
65 andreas 385
                if (!drawText(&target))
54 andreas 386
                    return;
387
            }
65 andreas 388
 
389
            SkImageInfo info = target.info();
390
            size_t rowBytes = info.minRowBytes();
391
            size_t size = info.computeByteSize(rowBytes);
392
 
43 andreas 393
#ifdef _SCALE_SKIA_
26 andreas 394
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
395
            {
396
                SkPaint paint;
65 andreas 397
                int left, top;
398
 
26 andreas 399
                paint.setBlendMode(SkBlendMode::kSrc);
400
                paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 401
                // Calculate new dimension
31 andreas 402
                double scaleFactor = gPageManager->getScaleFactor();
403
                MSG_DEBUG("Using scale factor " << scaleFactor);
404
                int lwidth = (int)((double)info.width() * scaleFactor);
405
                int lheight = (int)((double)info.height() * scaleFactor);
406
                int twidth = (int)((double)mSubpage.width * scaleFactor);
407
                int theight = (int)((double)mSubpage.height * scaleFactor);
65 andreas 408
                calcPosition(lwidth, lheight, &left, &top);
28 andreas 409
                // Create a canvas and draw new image
65 andreas 410
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
411
                target.allocN32Pixels(twidth, theight);
412
                target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
413
                SkCanvas can(target, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
31 andreas 414
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
26 andreas 415
                can.drawImageRect(im, rect, &paint);
65 andreas 416
                rowBytes = target.info().minRowBytes();
417
                size = target.info().computeByteSize(rowBytes);
31 andreas 418
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
26 andreas 419
            }
43 andreas 420
#endif
66 andreas 421
            if (mSubpage.sr[0].te.empty())
422
                _setBackground(handle, (unsigned char *)target.getPixels(), size, rowBytes, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
6 andreas 423
        }
424
    }
66 andreas 425
 
426
    if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].te.empty())
54 andreas 427
    {
428
        MSG_DEBUG("Drawing a text only on background image ...");
429
 
66 andreas 430
        if (!drawText(&target))
54 andreas 431
            return;
432
 
66 andreas 433
        SkImageInfo info = target.info();
54 andreas 434
        size_t rowBytes = info.minRowBytes();
435
        size_t size = info.computeByteSize(rowBytes);
66 andreas 436
        rowBytes = target.info().minRowBytes();
437
        size = target.info().computeByteSize(rowBytes);
438
        _setBackground(handle, (unsigned char *)target.getPixels(), size, rowBytes, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
439
        haveImage = true;
54 andreas 440
    }
66 andreas 441
 
442
    if (mSubpage.sr.size() > 0 && !haveImage)
6 andreas 443
    {
444
        MSG_DEBUG("Calling \"setBackground\" with no image ...");
38 andreas 445
        _setBackground(handle, nullptr, 0, 0, 0, 0, TColor::getColor(mSubpage.sr[0].cf));
6 andreas 446
    }
3 andreas 447
 
6 andreas 448
    // Draw the buttons
449
    BUTTONS_T *button = mButtons;
450
 
451
    while (button)
452
    {
453
        if (button->button)
454
        {
455
            MSG_DEBUG("Drawing button " << button->button->getButtonIndex() << ": " << button->button->getButtonName());
456
            button->button->registerCallback(_displayButton);
21 andreas 457
            button->button->regCallPlayVideo(_playVideo);
7 andreas 458
            button->button->setFonts(mFonts);
459
            button->button->setPalette(mPalette);
6 andreas 460
            button->button->createButtons();
10 andreas 461
 
462
            if (mSubpage.sr.size() > 0)
463
                button->button->setGlobalOpacity(mSubpage.sr[0].oo);
464
 
6 andreas 465
            button->button->show();
466
        }
467
 
468
        button = button->next;
3 andreas 469
    }
11 andreas 470
 
471
    // Mark page as visible
472
    mVisible = true;
3 andreas 473
}
474
 
11 andreas 475
void TSubPage::drop()
476
{
477
    DECL_TRACER("TSubPage::drop()");
478
 
14 andreas 479
    if (mVisible && _callDropSubPage)
11 andreas 480
        _callDropSubPage((mSubpage.pageID << 16) & 0xffff0000);
481
 
54 andreas 482
    stopTimer();
15 andreas 483
    // Set all elements of subpage invisible
484
    BUTTONS_T *bt = mButtons;
485
 
486
    while (bt)
487
    {
488
        bt->button->hide();
489
        bt = bt->next;
490
    }
491
 
14 andreas 492
    mZOrder = -1;
11 andreas 493
    mVisible = false;
494
}
495
 
3 andreas 496
BUTTONS_T *TSubPage::addButton(TButton* button)
497
{
498
    DECL_TRACER("*TSubPage::addButton(TButton* button)");
499
 
500
    if (!button)
501
    {
502
        MSG_ERROR("Parameter is NULL!");
503
        TError::setError();
504
        return nullptr;
505
    }
506
 
507
    try
508
    {
509
        BUTTONS_T *chain = new BUTTONS_T;
510
        chain->button = button;
14 andreas 511
        chain->next = nullptr;
512
        chain->previous = nullptr;
513
        BUTTONS_T *bts = mButtons;
3 andreas 514
 
14 andreas 515
        if (bts)
3 andreas 516
        {
14 andreas 517
            BUTTONS_T *p = bts;
3 andreas 518
 
39 andreas 519
            while (p && p->next)
3 andreas 520
                p = p->next;
521
 
522
            p->next = chain;
523
            chain->previous = p;
524
        }
525
        else
526
            mButtons = chain;
527
 
528
        return chain;
529
    }
530
    catch (std::exception& e)
531
    {
532
        MSG_ERROR("Memory error: " << e.what());
533
        TError::setError();
534
    }
535
 
536
    return nullptr;
537
}
538
 
53 andreas 539
void TSubPage::startTimer()
540
{
541
    DECL_TRACER("TSubPage::startTimer()");
542
 
543
    if (mSubpage.timeout <= 0 || mTimerRunning)
544
        return;
545
 
546
    try
547
    {
548
        mThreadTimer = std::thread([=] { runTimer(); });
549
        mThreadTimer.detach();
550
    }
551
    catch (std::exception& e)
552
    {
553
        MSG_ERROR("Error starting a timeout thread: " << e.what());
554
        return;
555
    }
556
}
557
 
558
void TSubPage::runTimer()
559
{
560
    DECL_TRACER("TSubPage::runTimer()");
561
 
93 andreas 562
    if (mTimerRunning)
563
        return;
564
 
53 andreas 565
    mTimerRunning = true;
566
    ulong tm = mSubpage.timeout * 100;
54 andreas 567
    ulong unit = 100;
568
    ulong total = 0;
569
 
570
    while (mTimerRunning && !prg_stopped && total < tm)
571
    {
572
        std::this_thread::sleep_for(std::chrono::milliseconds(unit));
573
        total += unit;
574
    }
575
 
576
    drop();
53 andreas 577
    mTimerRunning = false;
578
}
579
 
14 andreas 580
bool TSubPage::hasButton(int id)
581
{
582
    DECL_TRACER("TSubPage::hasButton(int id)");
583
 
584
    BUTTONS_T *bt = mButtons;
585
 
586
    while (bt)
587
    {
588
        if (bt->button && bt->button->getButtonIndex() == id)
589
            return true;
590
 
591
        bt = bt->next;
592
    }
593
 
594
    return false;
595
}
596
 
597
TButton *TSubPage::getButton(int id)
598
{
599
    DECL_TRACER("TSubPage::getButton(int id)");
600
 
601
    BUTTONS_T *bt = mButtons;
602
 
603
    while (bt)
604
    {
605
        if (bt->button && bt->button->getButtonIndex() == id)
606
            return bt->button;
607
 
608
        bt = bt->next;
609
    }
610
 
611
    return nullptr;
612
}
613
 
16 andreas 614
vector<TButton *> TSubPage::getButtons(int ap, int ad)
615
{
616
    DECL_TRACER("TSubPage::getButtons(int ap, int ad)");
617
 
618
    vector<TButton *> list;
619
    BUTTONS_T *bt = mButtons;
620
 
621
    while (bt)
622
    {
623
        if (bt->button->getAddressPort() == ap && bt->button->getAddressChannel() == ad)
624
            list.push_back(bt->button);
625
 
626
        bt = bt->next;
627
    }
628
 
629
    return list;
630
}
631
 
51 andreas 632
std::vector<TButton *> TSubPage::getAllButtons()
633
{
634
    DECL_TRACER("TSubPage::getAllButtons()");
635
 
636
    vector<TButton *> list;
637
    BUTTONS_T *bt = mButtons;
638
 
639
    while(bt)
640
    {
641
        list.push_back(bt->button);
642
        bt = bt->next;
643
    }
644
 
645
    return list;
646
}
647
 
3 andreas 648
/*
649
 * Sort the button according to their Z-order.
650
 * The button with the highest Z-order will be the last button in the chain.
651
 * The algorithm is a bubble sort algorithm.
652
 */
653
bool TSubPage::sortButtons()
654
{
655
    DECL_TRACER("TSubPage::sortButtons()");
656
 
657
    bool turned = true;
658
 
659
    while (turned)
660
    {
661
        BUTTONS_T *button = mButtons;
662
        turned = false;
663
 
664
        while (button)
665
        {
666
            int zo = button->button->getZOrder();
667
 
668
            if (button->previous)
669
            {
670
                if (zo < button->previous->button->getZOrder())
671
                {
672
                    BUTTONS_T *pprev = button->previous->previous;
673
                    BUTTONS_T *prev = button->previous;
674
                    BUTTONS_T *next = button->next;
675
 
676
                    if (pprev)
677
                        pprev->next = button;
678
 
679
                    prev->next = next;
680
                    prev->previous = button;
681
                    button->next = prev;
682
                    button->previous = pprev;
683
 
684
                    if (!pprev)
685
                        mButtons = button;
686
 
687
                    button = next;
688
 
689
                    if (next)
690
                        next->previous = prev;
691
 
692
                    turned = true;
693
                    continue;
694
                }
695
            }
696
 
697
            button = button->next;
698
        }
699
    }
700
 
701
    return true;
702
}
43 andreas 703
#ifdef _SCALE_SKIA_
31 andreas 704
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)
43 andreas 705
#else
706
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)
707
#endif
31 andreas 708
{
43 andreas 709
    DECL_TRACER("TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)");
31 andreas 710
 
711
    int nw = mSubpage.width;
712
    int nh = mSubpage.height;
43 andreas 713
#ifdef _SCALE_SKIA_
31 andreas 714
    if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
715
    {
716
        nw = (int)((double)mSubpage.width * gPageManager->getScaleFactor());
717
        nh = (int)((double)mSubpage.height * gPageManager->getScaleFactor());
718
    }
43 andreas 719
#endif
31 andreas 720
    switch (mSubpage.sr[0].jb)
721
    {
722
        case 0: // absolute position
723
            *left = mSubpage.sr[0].bx;
724
            *top = mSubpage.sr[0].by;
43 andreas 725
#ifdef _SCALE_SKIA_
31 andreas 726
            if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
727
            {
728
                *left = (int)((double)mSubpage.sr[0].bx * gPageManager->getScaleFactor());
729
                *left = (int)((double)mSubpage.sr[0].by * gPageManager->getScaleFactor());
730
            }
43 andreas 731
#endif
31 andreas 732
        break;
733
 
734
        case 1: // top, left
735
            *left = 0;
736
            *top = 0;
737
        break;
738
 
739
        case 2: // center, top
740
            *left = (nw - im_width) / 2;
741
            *top = 0;
742
        break;
743
 
744
        case 3: // right, top
745
            *left = nw - im_width;
746
            *top = 0;
747
        break;
748
 
749
        case 4: // left, middle
750
            *left = 0;
751
            *top = (nh - im_height) / 2;
752
        break;
753
 
754
        case 6: // right, middle
755
            *left = nw - im_width;
756
            *top = (nh - im_height) / 2;
757
        break;
758
 
759
        case 7: // left, bottom
760
            *left = 0;
761
            *top = nh - im_height;
762
        break;
763
 
764
        case 8: // center, bottom
765
            *left = (nw - im_width) / 2;
766
            *top = nh - im_height;
767
        break;
768
 
769
        case 9: // right, bottom
770
            *left = nw - im_width;
771
            *top = nh - im_height;
772
        break;
773
 
774
        default:    // center middle
775
            *left = (nw - im_width) / 2;
776
            *top = (nh - im_height) / 2;
777
    }
778
 
779
    if (*left < 0)
780
        *left = 0;
781
 
782
    if (*top < 0)
783
        *top = 0;
784
}
785
 
54 andreas 786
int TSubPage::numberLines(const string& str)
787
{
788
    DECL_TRACER("TButton::numberLines(const string& str)");
789
 
790
    int lines = 1;
791
 
792
    for (size_t i = 0; i < str.length(); i++)
793
    {
794
        if (str.at(i) == '\n')
795
            lines++;
796
    }
797
 
798
    return lines;
799
}
800
 
801
int TSubPage::calcLineHeight(string text, SkFont& font)
802
{
803
    DECL_TRACER("TButton::calcLineHeight(string text, SkFont& font)");
804
 
805
    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.c_str(), font);
806
    SkRect rect = blob.get()->bounds();
807
    return rect.height();
808
}
809
 
810
Button::POSITION_t TSubPage::calcImagePosition(int width, int height, Button::CENTER_CODE cc, int line)
811
{
812
    DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
813
 
814
    SR_T act_sr;
815
    POSITION_t position;
816
    int ix, iy;
817
 
818
    if (mSubpage.sr.size() == 0)
819
        return position;
820
 
821
    act_sr = mSubpage.sr.at(0);
822
//    int border_size = getBorderSize(act_sr.bs);
823
    int border_size = 0;
824
    int code, border = border_size;
825
    string dbgCC;
826
    int rwt = 0, rht = 0;
827
 
828
    switch (cc)
829
    {
830
        case SC_ICON:
831
            code = act_sr.ji;
832
            ix = act_sr.ix;
833
            iy = act_sr.iy;
834
            border = border_size = 0;
835
            dbgCC = "ICON";
836
            rwt = width;
837
            rht = height;
838
            break;
839
 
840
        case SC_BITMAP:
841
            code = act_sr.jb;
842
            ix = act_sr.bx;
843
            iy = act_sr.by;
844
            dbgCC = "BITMAP";
845
            rwt = std::min(mSubpage.width - border * 2, width);
846
            rht = std::min(mSubpage.height - border_size * 2, height);
847
            break;
848
 
849
        case SC_TEXT:
850
            code = act_sr.jt;
851
            ix = act_sr.tx;
852
            iy = act_sr.ty;
853
            dbgCC = "TEXT";
854
            border += 4;
855
            rwt = std::min(mSubpage.width - border * 2, width);
856
            rht = std::min(mSubpage.height - border_size * 2, height);
857
            break;
858
    }
859
 
860
    if (width > rwt || height > rht)
861
        position.overflow = true;
862
 
863
    switch (code)
864
    {
865
        case 0: // absolute position
866
            position.left = ix;
867
 
868
            if (cc == SC_TEXT)
869
                position.top = iy + height * line;
870
            else
871
                position.top = iy;
872
 
873
            if (cc == SC_BITMAP && ix < 0 && rwt < width)
874
                position.left *= -1;
875
 
876
            if (cc == SC_BITMAP && iy < 0 && rht < height)
877
                position.top += -1;
878
 
879
            position.width = rwt;
880
            position.height = rht;
881
            break;
882
 
883
        case 1: // top, left
884
            if (cc == SC_TEXT)
885
            {
886
                position.left = border;
887
                position.top = height * line;
888
            }
889
 
890
            position.width = rwt;
891
            position.height = rht;
892
            break;
893
 
894
        case 2: // center, top
895
            if (cc == SC_TEXT)
896
                position.top = height * line;
897
 
898
            position.left = (mSubpage.width - rwt) / 2;
899
            position.height = rht;
900
            position.width = rwt;
901
            break;
902
 
903
        case 3: // right, top
904
            position.left = mSubpage.width - rwt;
905
 
906
            if (cc == SC_TEXT)
907
            {
908
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
909
                position.top = height * line;
910
            }
911
 
912
            position.width = rwt;
913
            position.height = rht;
914
            break;
915
 
916
        case 4: // left, middle
917
            if (cc == SC_TEXT)
918
            {
919
                position.left = border;
920
                position.top = ((mSubpage.height - rht) / 2) + (height / 2 * line);
921
            }
922
            else
923
                position.top = (mSubpage.height - rht) / 2;
924
 
925
            position.width = rwt;
926
            position.height = rht;
927
            break;
928
 
929
        case 6: // right, middle
930
            position.left = mSubpage.width - rwt;
931
 
932
            if (cc == SC_TEXT)
933
            {
934
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
935
                position.top = ((mSubpage.height - rht) / 2) + (height / 2 * line);
936
            }
937
            else
938
                position.top = (mSubpage.height - rht) / 2;
939
 
940
            position.width = rwt;
941
            position.height = rht;
942
            break;
943
 
944
        case 7: // left, bottom
945
            if (cc == SC_TEXT)
946
            {
947
                position.left = border_size;
948
                position.top = (mSubpage.height - rht) - height * line;
949
            }
950
            else
951
                position.top = mSubpage.height - rht;
952
 
953
            position.width = rwt;
954
            position.height = rht;
955
            break;
956
 
957
        case 8: // center, bottom
958
            position.left = (mSubpage.width - rwt) / 2;
959
 
960
            if (cc == SC_TEXT)
961
                position.top = (mSubpage.height - rht) - height * line;
962
            else
963
                position.top = mSubpage.height - rht;
964
 
965
            position.width = rwt;
966
            position.height = rht;
967
            break;
968
 
969
        case 9: // right, bottom
970
            position.left = mSubpage.width - rwt;
971
 
972
            if (cc == SC_TEXT)
973
            {
974
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
975
                position.top = (mSubpage.height - rht) - height * line;
976
            }
977
            else
978
                position.top = mSubpage.height - rht;
979
            break;
980
 
981
        default: // center, middle
982
            position.left = (mSubpage.width - rwt) / 2;
983
 
984
            if (cc == SC_TEXT)
985
                position.top = ((mSubpage.height - rht) / 2) + (height / 2 * line);
986
            else
987
                position.top = (mSubpage.height - rht) / 2;
988
 
989
            position.width = rwt;
990
            position.height = rht;
991
    }
992
 
993
    MSG_DEBUG("Type: " << dbgCC << ", PosType=" << code << ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width << ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
994
    position.valid = true;
995
    return position;
996
}
997
 
998
bool TSubPage::drawText(SkBitmap *img)
999
{
1000
    MSG_TRACE("TSubPage::drawText(SkImage& img)");
1001
 
1002
    if (mSubpage.sr[0].te.empty())
1003
        return true;
1004
 
1005
    MSG_DEBUG("Searching for font number " << mSubpage.sr[0].fi << " with text " << mSubpage.sr[0].te);
1006
    FONT_T font = mFonts->getFont(mSubpage.sr[0].fi);
1007
 
1008
    if (!font.file.empty())
1009
    {
1010
        SkCanvas canvas(*img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1011
        sk_sp<SkTypeface> typeFace = mFonts->getTypeFace(mSubpage.sr[0].fi);
1012
 
1013
        if (!typeFace)
1014
        {
1015
            MSG_ERROR("Error creating type face " << font.fullName);
1016
            TError::setError();
1017
            return false;
1018
        }
1019
 
1020
        SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
1021
        SkFont skFont(typeFace, fontSizePt);
1022
 
1023
        SkPaint paint;
1024
        paint.setAntiAlias(true);
1025
        SkColor color = TColor::getSkiaColor(mSubpage.sr[0].ct);
1026
        paint.setColor(color);
1027
        paint.setStyle(SkPaint::kFill_Style);
1028
 
1029
        SkFontMetrics metrics;
1030
        skFont.getMetrics(&metrics);
1031
        int lines = numberLines(mSubpage.sr[0].te);
1032
 
1033
        if (lines > 1 || mSubpage.sr[0].ww)
1034
        {
1035
            vector<string> textLines;
1036
 
1037
            if (!mSubpage.sr[0].ww)
1038
                textLines = ::splitLine(mSubpage.sr[0].te);
1039
            else
1040
            {
1041
                textLines = splitLine(mSubpage.sr[0].te, mSubpage.width, mSubpage.height, skFont, paint);
1042
                lines = textLines.size();
1043
            }
1044
 
1045
            int lineHeight = calcLineHeight(mSubpage.sr[0].te, skFont);
1046
            int totalHeight = lineHeight * lines;
1047
 
1048
            if (totalHeight > mSubpage.height)
1049
            {
1050
                lines = mSubpage.height / lineHeight;
1051
                totalHeight = lineHeight * lines;
1052
            }
1053
 
1054
            MSG_DEBUG("Line height: " << lineHeight);
1055
            POSITION_t position = calcImagePosition(mSubpage.width, totalHeight, SC_TEXT, 1);
1056
            MSG_DEBUG("Position frame: l: " << position.left << ", t: " << position.top << ", w: " << position.width << ", h: " << position.height);
1057
 
1058
            if (!position.valid)
1059
            {
1060
                MSG_ERROR("Error calculating the text position!");
1061
                TError::setError();
1062
                return false;
1063
            }
1064
 
1065
            vector<string>::iterator iter;
1066
            int line = 0;
1067
 
1068
            for (iter = textLines.begin(); iter != textLines.end(); iter++)
1069
            {
1070
                sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
1071
                SkRect rect;
1072
                skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
1073
                POSITION_t pos = calcImagePosition(rect.width(), lineHeight, SC_TEXT, 1);
1074
 
1075
                if (!pos.valid)
1076
                {
1077
                    MSG_ERROR("Error calculating the text position!");
1078
                    TError::setError();
1079
                    return false;
1080
                }
1081
                MSG_DEBUG("Triing to print line: " << *iter);
1082
 
1083
                SkScalar startX = (SkScalar)pos.left;
1084
                SkScalar startY = (SkScalar)position.top + lineHeight * line;
1085
                MSG_DEBUG("x=" << startX << ", y=" << startY);
1086
                canvas.drawTextBlob(blob, startX, startY + lineHeight / 2 + 4, paint);
1087
                line++;
1088
 
1089
                if (line > lines)
1090
                    break;
1091
            }
1092
        }
1093
        else    // single line
1094
        {
1095
            sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(mSubpage.sr[0].te.c_str(), skFont);
1096
            SkRect rect;
1097
            skFont.measureText(mSubpage.sr[0].te.c_str(), mSubpage.sr[0].te.length(), SkTextEncoding::kUTF8, &rect, &paint);
1098
            POSITION_t position = calcImagePosition(rect.width(), (rect.height() * (float)lines), SC_TEXT, 0);
1099
 
1100
            if (!position.valid)
1101
            {
1102
                MSG_ERROR("Error calculating the text position!");
1103
                TError::setError();
1104
                return false;
1105
            }
1106
 
1107
            MSG_DEBUG("Printing line " << mSubpage.sr[0].te);
1108
            SkScalar startX = (SkScalar)position.left;
1109
            SkScalar startY = (SkScalar)position.top + metrics.fCapHeight; // + metrics.fLeading; // (metrics.fAscent * -1.0);
1110
            canvas.drawTextBlob(blob, startX, startY, paint);
1111
        }
1112
    }
1113
    else
1114
    {
1115
        MSG_WARNING("No font file name found for font " << mSubpage.sr[0].fi);
1116
    }
1117
 
1118
    return true;
1119
}
1120
 
11 andreas 1121
RECT_T TSubPage::getRegion()
1122
{
1123
    DECL_TRACER("TSubPage::getRegion()");
1124
    return {mSubpage.left, mSubpage.top, mSubpage.width, mSubpage.height};
1125
}
1126
 
15 andreas 1127
/**
1128
 * This method is called indirectly from the GUI after a mouse click. If This
1129
 * subpage matches the clicked coordinates, than the elements are tested. If
1130
 * an element is found that matches the coordinates it gets the click. It
1131
 * depends on the kind of element what happens.
1132
 */
11 andreas 1133
void TSubPage::doClick(int x, int y, bool pressed)
1134
{
1135
    DECL_TRACER("TSubPage::doClick(int x, int y)");
1136
 
1137
    BUTTONS_T *button = mButtons;
1138
    // Find last button
39 andreas 1139
    while (button && button->next)
11 andreas 1140
        button = button->next;
1141
 
1142
    // Scan in reverse order
1143
    while (button)
1144
    {
1145
        TButton *but = button->button;
1146
 
31 andreas 1147
        if (x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
1148
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
11 andreas 1149
        {
1150
            MSG_DEBUG("Clicking button " << but->getButtonIndex() << ": " << but->getButtonName() << " to state " << (pressed ? "PRESS" : "RELEASE"));
15 andreas 1151
            int btX = x - but->getLeftPosition();
1152
            int btY = y - but->getTopPosition();
1153
 
1154
            if (but->doClick(btX, btY, pressed))
1155
                break;
11 andreas 1156
        }
1157
 
1158
        button = button->previous;
1159
    }
1160
}