Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
15 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
2 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
 */
3 andreas 18
 
19
#include <string>
20
#include <memory>
6 andreas 21
 
22
#include <include/core/SkPixmap.h>
3 andreas 23
#include <include/core/SkSize.h>
4 andreas 24
#include <include/core/SkColor.h>
7 andreas 25
#include <include/core/SkFont.h>
26
#include <include/core/SkTypeface.h>
8 andreas 27
#include <include/core/SkFontMetrics.h>
19 andreas 28
#include <include/core/SkTextBlob.h>
8 andreas 29
#include <include/core/SkRegion.h>
30
#include <include/core/SkImageFilter.h>
31
#include <include/effects/SkImageFilters.h>
32
#include <include/core/SkPath.h>
10 andreas 33
#include <include/core/SkSurfaceProps.h>
6 andreas 34
 
2 andreas 35
#include "tbutton.h"
3 andreas 36
#include "terror.h"
37
#include "tconfig.h"
4 andreas 38
#include "tresources.h"
8 andreas 39
#include "ticons.h"
14 andreas 40
#include "tamxnet.h"
41
#include "tobject.h"
42
#include "tpagemanager.h"
2 andreas 43
 
15 andreas 44
using std::exception;
3 andreas 45
using std::string;
46
using std::vector;
47
using std::unique_ptr;
48
using std::map;
49
using std::pair;
15 andreas 50
using std::thread;
51
using std::atomic;
52
using std::mutex;
21 andreas 53
using std::bind;
2 andreas 54
using namespace Button;
55
 
21 andreas 56
#define MAX_BUFFER      65536
57
 
8 andreas 58
extern TIcons *gIcons;
14 andreas 59
extern amx::TAmxNet *gAmxNet;
8 andreas 60
 
21 andreas 61
atomic<bool> TButton::mAniRunning;
62
THR_REFRESH_t *TButton::mThrRefresh = nullptr;
63
 
15 andreas 64
mutex mutex_button;
65
mutex mutex_bargraph;
66
mutex mutex_sysdraw;
67
 
4 andreas 68
SYSBORDER_t sysBorders[] = {
8 andreas 69
    {  1, (char *)"Single Line",         0, (char *)"solid",   1,   0 },
70
    {  2, (char *)"Double Line",         0, (char *)"solid",   2,   0 },
71
    {  3, (char *)"Quad Line",           0, (char *)"solid",   4,   0 },
72
    {  4, (char *)"Picture Frame",       0, (char *)"double",  0,   0 },
73
    {  5, (char *)"Circle 15",           8, (char *)"solid",   2,  15 },
74
    {  6, (char *)"Circle 25",           9, (char *)"solid",   2,  25 },
75
    {  7, (char *)"Circle 35",          10, (char *)"solid",   2,  35 },
76
    {  8, (char *)"Circle 45",          11, (char *)"solid",   2,  45 },
77
    {  9, (char *)"Circle 55",          12, (char *)"solid",   2,  55 },
78
    { 10, (char *)"Circle 65",          13, (char *)"solid",   2,  65 },
79
    { 11, (char *)"Circle 75",          14, (char *)"solid",   2,  75 },
80
    { 12, (char *)"Circle 85",          15, (char *)"solid",   2,  85 },
81
    { 13, (char *)"Circle 95",          16, (char *)"solid",   2,  95 },
82
    { 14, (char *)"Circle 105",         17, (char *)"solid",   2, 105 },
83
    { 15, (char *)"Circle 115",         18, (char *)"solid",   2, 115 },
84
    { 16, (char *)"Circle 125",         19, (char *)"solid",   2, 125 },
85
    { 17, (char *)"Circle 135",         20, (char *)"solid",   2, 135 },
86
    { 18, (char *)"Circle 145",         21, (char *)"solid",   2, 145 },
87
    { 19, (char *)"Circle 155",         22, (char *)"solid",   2, 155 },
88
    { 20, (char *)"Circle 165",         23, (char *)"solid",   2, 165 },
89
    { 21, (char *)"Circle 175",         24, (char *)"solid",   2, 175 },
90
    { 22, (char *)"Circle 185",         25, (char *)"solid",   2, 185 },
91
    { 23, (char *)"Circle 195",         26, (char *)"solid",   2, 195 },
92
    { 24, (char *)"AMX Elite Inset _L",  0, (char *)"groove", 10,   0 },
93
    { 25, (char *)"AMX Elite Raised _L", 0, (char *)"ridge",  10,   0 },
94
    { 26, (char *)"AMX Elite Inset _M",  0, (char *)"groove",  5,   0 },
95
    { 27, (char *)"AMX Elite Raised _M", 0, (char *)"ridge",   5,   0 },
96
    { 28, (char *)"AMX Elite Inset _S",  0, (char *)"groove",  2,   0 },
97
    { 29, (char *)"AMX Elite Raised _S", 0, (char *)"ridge",   2,   0 },
98
    { 30, (char *)"Bevel Inset _L",      0, (char *)"inset",  10,   0 },
99
    { 31, (char *)"Bevel Raised _L",     0, (char *)"outset", 10,   0 },
100
    { 32, (char *)"Bevel Inset _M",      0, (char *)"inset",   5,   0 },
101
    { 33, (char *)"Bevel Raised _M",     0, (char *)"outset",  5,   0 },
102
    { 34, (char *)"Bevel Inset _S",      0, (char *)"inset",   2,   0 },
103
    { 35, (char *)"Bevel Raised _S",     0, (char *)"outset",  2,   0 },
104
    {  0, nullptr, 0, nullptr, 0, 0 }
4 andreas 105
};
106
 
15 andreas 107
SYSBUTTONS_t sysButtons[] = {
108
    {   8, MULTISTATE_BARGRAPH,  12, 0,  11 },  // Connection status
109
    { 141, GENERAL,               2, 0,   0 },  // Standard time
110
    { 142, GENERAL,               2, 0,   0 },  // Time AM/PM
111
    { 143, GENERAL,               2, 0,   0 },  // 24 hour time
112
    { 151, GENERAL,               2, 0,   0 },  // Date weekday
113
    { 152, GENERAL,               2, 0,   0 },  // Date mm/dd
114
    { 153, GENERAL,               2, 0,   0 },  // Date dd/mm
115
    { 154, GENERAL,               2, 0,   0 },  // Date mm/dd/yyyy
116
    { 155, GENERAL,               2, 0,   0 },  // Date dd/mm/yyyy
117
    { 156, GENERAL,               2, 0,   0 },  // Date month dd, yyyy
118
    { 157, GENERAL,               2, 0,   0 },  // Date dd month, yyyy
119
    { 158, GENERAL,               2, 0,   0 },  // Date yyyy-mm-dd
120
    { 234, GENERAL,               2, 0,   0 },  // Battery charging/not charging
121
    { 242, BARGRAPH,              2, 0, 100 },  // Battery level
122
    {   0, NONE,                  0, 0,   0 }   // Terminate
123
};
4 andreas 124
 
3 andreas 125
TButton::TButton()
2 andreas 126
{
3 andreas 127
    DECL_TRACER("TButton::TButton()");
16 andreas 128
 
21 andreas 129
    mAniRunning = false;
16 andreas 130
    mLastBlink.clear();
2 andreas 131
}
132
 
3 andreas 133
TButton::~TButton()
2 andreas 134
{
3 andreas 135
    DECL_TRACER("TButton::~TButton()");
136
 
137
    map<int, IMAGE_t>::iterator iter;
138
 
139
    for (iter = mImages.begin(); iter != mImages.end(); iter++)
140
    {
6 andreas 141
        if (!iter->second.imageMi.empty())
3 andreas 142
            iter->second.imageMi.reset();
143
 
6 andreas 144
        if (!iter->second.imageBm.empty())
3 andreas 145
            iter->second.imageBm.reset();
146
    }
147
 
148
    mImages.clear();
15 andreas 149
 
150
    if (ap == 0 && ad == 8)
151
    {
152
        if (gAmxNet)
153
            gAmxNet->deregNetworkState(mHandle);
154
    }
155
 
156
    if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158)))
157
    {
158
        if (gAmxNet)
159
            gAmxNet->deregTimer(mHandle);
160
    }
161
 
162
    if (mTimer)
163
    {
164
        mTimer->stop();
165
 
166
        while (mTimer->isRunning())
167
            usleep(50);
168
 
169
        delete mTimer;
170
    }
21 andreas 171
 
172
    THR_REFRESH_t *next, *p = mThrRefresh;
173
 
174
    while (p)
175
    {
176
        if (p->mImageRefresh)
177
        {
178
            p->mImageRefresh->stop();
179
 
180
            while (p->mImageRefresh->isRunning())
181
                usleep(50);
182
 
183
            delete p->mImageRefresh;
184
            p->mImageRefresh = nullptr;
185
        }
186
 
187
        next = p->next;
188
        delete p;
189
        p = next;
190
    }
2 andreas 191
}
3 andreas 192
 
193
void TButton::initialize(TReadXML *xml, mxml_node_t *node)
194
{
195
    DECL_TRACER("TButton::initialize(TReadXML *xml, mxml_node_t *node)");
196
 
197
    if (!xml || !node)
198
    {
199
        MSG_ERROR("Invalid NULL parameter passed!");
200
        TError::setError();
201
        return;
202
    }
203
 
204
    mxml_node_t *child = xml->getFirstChild(node);
205
    string stype = xml->getAttributeFromNode(node, "type");
206
    type = getButtonType(stype);
207
    MSG_DEBUG("Button type: " << stype << " --> " << type);
208
 
209
    while(child)
210
    {
211
        string ename = xml->getElementName(child);
212
 
213
        if (ename.compare("bi") == 0)
214
            bi = xml->getIntFromNode(child);
215
        else if (ename.compare("na") == 0)
216
            na = xml->getTextFromNode(child);
217
        else if (ename.compare("bd") == 0)
218
            bd = xml->getTextFromNode(child);
219
        else if (ename.compare("lt") == 0)
220
            lt = xml->getIntFromNode(child);
221
        else if (ename.compare("tp") == 0)
222
            tp = xml->getIntFromNode(child);
223
        else if (ename.compare("wt") == 0)
224
            wt = xml->getIntFromNode(child);
225
        else if (ename.compare("ht") == 0)
226
            ht = xml->getIntFromNode(child);
227
        else if (ename.compare("zo") == 0)
228
            zo = xml->getIntFromNode(child);
229
        else if (ename.compare("hs") == 0)
230
            hs = xml->getTextFromNode(child);
231
        else if (ename.compare("bs") == 0)
232
            bs = xml->getTextFromNode(child);
233
        else if (ename.compare("fb") == 0)
234
            fb = getButtonFeedback(xml->getTextFromNode(child));
235
        else if (ename.compare("ap") == 0)
236
            ap = xml->getIntFromNode(child);
237
        else if (ename.compare("ad") == 0)
238
            ad = xml->getIntFromNode(child);
239
        else if (ename.compare("ch") == 0)
240
            ch = xml->getIntFromNode(child);
241
        else if (ename.compare("cp") == 0)
242
            cp = xml->getIntFromNode(child);
243
        else if (ename.compare("lp") == 0)
244
            lp = xml->getIntFromNode(child);
245
        else if (ename.compare("lv") == 0)
246
            lv = xml->getIntFromNode(child);
247
        else if (ename.compare("dr") == 0)
248
            dr = xml->getTextFromNode(child);
249
        else if (ename.compare("va") == 0)
250
            va = xml->getIntFromNode(child);
251
        else if (ename.compare("rm") == 0)
252
            rm = xml->getIntFromNode(child);
253
        else if (ename.compare("nu") == 0)
254
            nu = xml->getIntFromNode(child);
255
        else if (ename.compare("nd") == 0)
256
            nd = xml->getIntFromNode(child);
257
        else if (ename.compare("ar") == 0)
258
            ar = xml->getIntFromNode(child);
259
        else if (ename.compare("ru") == 0)
260
            ru = xml->getIntFromNode(child);
261
        else if (ename.compare("rd") == 0)
262
            rd = xml->getIntFromNode(child);
263
        else if (ename.compare("lu") == 0)
264
            lu = xml->getIntFromNode(child);
265
        else if (ename.compare("ld") == 0)
266
            ld = xml->getIntFromNode(child);
267
        else if (ename.compare("rv") == 0)
268
            rv = xml->getIntFromNode(child);
269
        else if (ename.compare("rl") == 0)
270
            rl = xml->getIntFromNode(child);
271
        else if (ename.compare("rh") == 0)
272
            rh = xml->getIntFromNode(child);
273
        else if (ename.compare("ri") == 0)
274
            ri = xml->getIntFromNode(child);
275
        else if (ename.compare("rn") == 0)
276
            rn = xml->getIntFromNode(child);
277
        else if (ename.compare("if") == 0)
278
            _if = xml->getTextFromNode(child);
279
        else if (ename.compare("sd") == 0)
280
            sd = xml->getTextFromNode(child);
281
        else if (ename.compare("sc") == 0)
282
            sc = xml->getTextFromNode(child);
283
        else if (ename.compare("mt") == 0)
284
            mt = xml->getIntFromNode(child);
285
        else if (ename.compare("dt") == 0)
286
            dt = xml->getTextFromNode(child);
287
        else if (ename.compare("im") == 0)
288
            im = xml->getTextFromNode(child);
289
        else if (ename.compare("op") == 0)
290
            op = xml->getTextFromNode(child);
291
        else if (ename.compare("pf") == 0)
292
        {
293
            PUSH_FUNC_T pf;
294
            pf.pfName = xml->getTextFromNode(child);
295
            pf.pfType = xml->getAttributeFromNode(child, "type");
296
            pushFunc.push_back(pf);
297
        }
298
        else if (ename.compare("sr") == 0)
299
        {
300
            mxml_node_t *n = xml->getFirstChild(child);
301
            SR_T bsr;
302
            bsr.number = atoi(xml->getAttributeFromNode(child, "number").c_str());
303
 
304
            while (n)
305
            {
306
                string e = xml->getElementName(n);
307
 
308
                if (e.compare("do") == 0)
309
                    bsr._do = xml->getTextFromNode(n);
310
                else if (e.compare("bs") == 0)
311
                    bsr.bs = xml->getTextFromNode(n);
312
                else if (e.compare("mi") == 0)
313
                    bsr.mi = xml->getTextFromNode(n);
314
                else if (e.compare("cb") == 0)
315
                    bsr.cb = xml->getTextFromNode(n);
316
                else if (e.compare("cf") == 0)
317
                    bsr.cf = xml->getTextFromNode(n);
318
                else if (e.compare("ct") == 0)
319
                    bsr.ct = xml->getTextFromNode(n);
320
                else if (e.compare("ec") == 0)
321
                    bsr.ec = xml->getTextFromNode(n);
322
                else if (e.compare("bm") == 0)
323
                {
324
                    bsr.bm = xml->getTextFromNode(n);
325
                    bsr.dynamic = ((atoi(xml->getAttributeFromNode(n, "dynamic").c_str()) == 1) ? true : false);
326
                }
327
                else if (e.compare("sd") == 0)
328
                    bsr.sd = xml->getTextFromNode(n);
329
                else if (e.compare("sb") == 0)
330
                    bsr.sb = xml->getIntFromNode(n);
331
                else if (e.compare("ii") == 0)
332
                    bsr.ii = xml->getIntFromNode(n);
333
                else if (e.compare("ji") == 0)
334
                    bsr.ji = xml->getIntFromNode(n);
335
                else if (e.compare("jb") == 0)
336
                    bsr.jb = xml->getIntFromNode(n);
10 andreas 337
                else if (e.compare("bx") == 0)
338
                    bsr.bx = xml->getIntFromNode(n);
339
                else if (e.compare("by") == 0)
340
                    bsr.by = xml->getIntFromNode(n);
3 andreas 341
                else if (e.compare("ix") == 0)
342
                    bsr.ix = xml->getIntFromNode(n);
343
                else if (e.compare("iy") == 0)
344
                    bsr.iy = xml->getIntFromNode(n);
345
                else if (e.compare("fi") == 0)
346
                    bsr.fi = xml->getIntFromNode(n);
347
                else if (e.compare("te") == 0)
348
                    bsr.te = xml->getTextFromNode(n);
349
                else if (e.compare("jt") == 0)
350
                    bsr.jt = (TEXT_ORIENTATION)xml->getIntFromNode(n);
351
                else if (e.compare("tx") == 0)
352
                    bsr.tx = xml->getIntFromNode(n);
353
                else if (e.compare("ty") == 0)
354
                    bsr.ty = xml->getIntFromNode(n);
355
                else if (e.compare("ww") == 0)
356
                    bsr.ww = xml->getIntFromNode(n);
357
                else if (e.compare("et") == 0)
358
                    bsr.et = xml->getIntFromNode(n);
359
                else if (e.compare("oo") == 0)
360
                    bsr.oo = xml->getIntFromNode(n);
361
 
362
                n = xml->getNextChild(n);
363
            }
364
 
365
            sr.push_back(bsr);
366
        }
367
 
368
        child = xml->getNextChild(child);
369
 
370
        if (xml->getElementName(child).compare("button") == 0)
371
        {
372
            MSG_WARNING("Unexpected element \"button\" found! Please check XML file.");
373
            break;
374
        }
375
    }
376
}
377
 
378
BUTTONTYPE TButton::getButtonType(const string& bt)
379
{
380
    DECL_TRACER("TButton::getButtonType(const string& bt)");
381
 
382
    if (bt.compare("general") == 0)
383
        return GENERAL;
384
    else if (bt.compare("multi-state general") == 0)
385
        return MULTISTATE_GENERAL;
386
    else if (bt.compare("bargraph") == 0)
387
        return BARGRAPH;
388
    else if (bt.compare("multi-state bargraph") == 0)
389
        return MULTISTATE_BARGRAPH;
390
    else if (bt.compare("joistick") == 0)
391
        return JOISTICK;
392
    else if (bt.compare("text input") == 0)
393
        return TEXT_INPUT;
394
    else if (bt.compare("computer control") == 0)
395
        return COMPUTER_CONTROL;
396
    else if (bt.compare("take note") == 0)
397
        return TAKE_NOTE;
398
    else if (bt.compare("sub-page view") == 0)
399
        return SUBPAGE_VIEW;
400
 
401
    return NONE;
402
}
403
 
404
FEEDBACK TButton::getButtonFeedback(const string& fb)
405
{
406
    DECL_TRACER("TButton::getButtonFeedback(const string& fb)");
407
 
408
    if (fb.compare("channel") == 0)
409
        return FB_CHANNEL;
410
    else if (fb.compare("inverted channel") == 0)
411
        return FB_INV_CHANNEL;
412
    else if (fb.compare("always on") == 0)
413
        return FB_ALWAYS_ON;
414
    else if (fb.compare("momentary") == 0)
415
        return FB_MOMENTARY;
416
    else if (fb.compare("blink") == 0)
417
        return FB_BLINK;
418
 
419
    return FB_NONE;
420
}
421
 
4 andreas 422
bool TButton::createButtons()
3 andreas 423
{
6 andreas 424
    DECL_TRACER("TButton::createButtons()");
3 andreas 425
 
426
    // Get the images, if there any
427
    vector<SR_T>::iterator srIter;
6 andreas 428
    int i = 0;
3 andreas 429
 
430
    for (srIter = sr.begin(); srIter != sr.end(); srIter++)
431
    {
432
        int number = srIter->number;
433
        IMAGE_t img;
434
 
21 andreas 435
        if (srIter->sb > 0)
436
            continue;
437
 
3 andreas 438
        if (!srIter->mi.empty())        // Do we have a chameleon image?
439
        {
4 andreas 440
            sk_sp<SkData> image;
441
 
442
            if (!(image = readImage(srIter->mi)))
3 andreas 443
                return false;
444
 
6 andreas 445
            DecodeDataToBitmap(image, &img.imageMi);
3 andreas 446
 
6 andreas 447
            if (img.imageMi.empty())
3 andreas 448
            {
449
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
450
                return false;
451
            }
452
 
6 andreas 453
            srIter->mi_width = img.imageMi.dimensions().width();
454
            srIter->mi_height = img.imageMi.dimensions().height();
3 andreas 455
        }
456
 
457
        if (!srIter->bm.empty())        // Do we have a bitmap?
458
        {
4 andreas 459
            sk_sp<SkData> image;
460
 
461
            if (!(image = readImage(srIter->bm)))
3 andreas 462
                return false;
463
 
6 andreas 464
            DecodeDataToBitmap(image, &img.imageBm);
3 andreas 465
 
6 andreas 466
            if (img.imageBm.empty())
3 andreas 467
            {
468
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
469
                return false;
470
            }
471
 
6 andreas 472
            srIter->bm_width = img.imageBm.dimensions().width();
473
            srIter->bm_height = img.imageBm.dimensions().height();
3 andreas 474
        }
475
 
476
        mImages.insert(pair<int, IMAGE_t>(number, img));
6 andreas 477
        i++;
3 andreas 478
    }
479
 
4 andreas 480
    return true;
481
}
3 andreas 482
 
16 andreas 483
void TButton::refresh()
484
{
485
    DECL_TRACER("TButton::refresh()");
486
 
487
    makeElement();
488
}
489
 
15 andreas 490
bool TButton::makeElement(int instance)
491
{
492
    DECL_TRACER("TButton::makeElement()");
493
 
494
    int inst = mActInstance;
495
 
496
    if (instance >= 0)
497
        inst = instance;
498
 
499
    if (type == MULTISTATE_GENERAL && ar == 1)
500
        return drawButtonMultistateAni();
501
    else if (type == BARGRAPH)
502
        return drawBargraph(inst, mLastLevel);
503
    else
504
        return drawButton(inst);
505
 
506
    return false;
507
}
508
 
14 andreas 509
bool TButton::setActive(int instance)
510
{
511
    DECL_TRACER("TButton::setActive(int instance)");
512
 
513
    if ((size_t)instance >= sr.size())
514
    {
515
        MSG_ERROR("Instance " << instance << " is higher than the maximum instance " << sr.size() << "!");
516
        return false;
517
    }
518
 
519
    if (instance == mActInstance)
520
    {
521
        MSG_TRACE("Not necessary to set instance " << instance << " again.");
522
        return true;
523
    }
524
 
525
    mActInstance = instance;
15 andreas 526
    makeElement(instance);
14 andreas 527
 
528
    return true;
529
}
530
 
531
bool TButton::setIcon(int id, int instance)
532
{
533
    DECL_TRACER("TButton::setIcon(int id, int instance)");
534
 
535
    if ((size_t)instance >= sr.size())
536
    {
537
        MSG_ERROR("Instance " << instance << " does not exist!");
538
        return false;
539
    }
540
 
541
    sr[instance].ii = id;
16 andreas 542
    return makeElement(instance);
14 andreas 543
}
544
 
545
bool TButton::setIcon(const string& icon, int instance)
546
{
547
    DECL_TRACER("TButton::setIcon(const string& icon, int instance)");
548
 
549
    if ((size_t)instance >= sr.size())
550
    {
551
        MSG_ERROR("Instance " << instance << " does not exist!");
552
        return false;
553
    }
554
 
555
    if (!gIcons)
556
    {
557
        gIcons = new TIcons();
558
 
559
        if (TError::isError())
560
        {
561
            MSG_ERROR("Error initializing icons!");
562
            return false;
563
        }
564
    }
565
 
566
    int id = gIcons->getNumber(icon);
567
 
568
    if (id == -1)
569
    {
570
        MSG_WARNING("Icon " << icon << " not found!");
571
        return false;
572
    }
573
 
574
    sr[instance].ii = id;
16 andreas 575
    return makeElement(instance);
14 andreas 576
}
577
 
578
bool TButton::revokeIcon(int instance)
579
{
580
    DECL_TRACER("TButton::revokeIcon(int instance)");
581
 
582
    if ((size_t)instance >= sr.size())
583
    {
584
        MSG_ERROR("Instance " << instance << " does not exist!");
585
        return false;
586
    }
587
 
588
    sr[instance].ii = 0;
16 andreas 589
    return makeElement(instance);
14 andreas 590
}
591
 
592
bool TButton::setText(const string& txt, int instance)
593
{
594
    DECL_TRACER("TButton::setText(const string& txt, int instance)");
595
 
596
    if ((size_t)instance >= sr.size())
597
    {
598
        MSG_ERROR("Instance " << instance << " does not exist!");
599
        return false;
600
    }
601
 
602
    sr[instance].te = txt;
16 andreas 603
    return makeElement(instance);
14 andreas 604
}
605
 
16 andreas 606
bool TButton::setBitmap(const string& file, int instance)
607
{
608
    DECL_TRACER("TButton::setBitmap(const string& file, int instance)");
609
 
610
    if (file.empty() || instance >= (int)sr.size())
611
    {
612
        MSG_ERROR("Invalid parameters!");
613
        return false;
614
    }
615
 
616
    sr[instance].bm = file;
617
    map<int, IMAGE_t>::iterator iter = mImages.find(sr[instance].number);
618
 
619
    if (iter != mImages.end())
620
        mImages.erase(iter);
621
 
622
    if (!createButtons())
623
        return false;
624
 
625
    return makeElement(instance);
626
}
627
 
628
bool TButton::setOpacity(int op, int instance)
629
{
630
    DECL_TRACER("TButton::setOpacity(int op, int instance)");
631
 
632
    if ((size_t)instance >= sr.size())
633
    {
634
        MSG_ERROR("Instance " << instance << " does not exist!");
635
        return false;
636
    }
637
 
638
    if (op < 0 || op < 255)
639
    {
640
        MSG_ERROR("Invalid opacity " << op << "!");
641
        return false;
642
    }
643
 
644
    sr[instance].oo = op;
645
    return makeElement(instance);
646
}
647
 
648
bool TButton::setFont(int id, int instance)
649
{
650
    DECL_TRACER("TButton::setFont(int id)");
651
 
652
    if (instance < 0 || (size_t)instance >= sr.size())
653
    {
654
        MSG_ERROR("Instance " << instance << " does not exist!");
655
        return false;
656
    }
657
 
658
    sr[instance].fi = id;
659
    return makeElement(instance);
660
}
661
 
662
void TButton::setLeft(int left)
663
{
664
    DECL_TRACER("TButton::setLeft(int left)");
665
 
666
    if (left < 0)
667
        return;
668
 
669
    lt = left;
670
    makeElement(mActInstance);
671
}
672
 
673
void TButton::setTop(int top)
674
{
675
    DECL_TRACER("TButton::setTop(int top)");
676
 
677
    if (top < 0)
678
        return;
679
 
680
    tp = top;
681
    makeElement(mActInstance);
682
}
683
 
684
void TButton::setLeftTop(int left, int top)
685
{
686
    DECL_TRACER("TButton::setLeftTop(int left, int top)");
687
 
688
    if (top < 0 || left < 0)
689
        return;
690
 
691
    lt = left;
692
    tp = top;
693
    makeElement(mActInstance);
694
}
695
 
21 andreas 696
void TButton::setResourceName(const string& name, int instance)
697
{
698
    DECL_TRACER("TButton::setResourceName(const string& name, int instance)");
699
 
700
    if (instance < 0 || instance >= (int)sr.size())
701
    {
702
        MSG_ERROR("Invalid instance " << instance);
703
        return;
704
    }
705
 
706
    if (!sr[instance].dynamic)
707
    {
708
        MSG_ERROR("Button " << bi << ": " << na << " is not a resource button!");
709
        return;
710
    }
711
 
712
    sr[instance].bm = name;
713
}
714
 
16 andreas 715
bool TButton::setWorWrap(bool state, int instance)
716
{
717
    DECL_TRACER("TButton::setWorWrap(bool state, int instance)");
718
 
719
    if (instance < 0 || instance >= (int)sr.size())
720
    {
721
        MSG_ERROR("Invalid instance " << instance);
722
        return false;
723
    }
724
 
725
    sr[instance].ww = state ? 1 : 0;
726
    return makeElement(instance);
727
}
728
 
15 andreas 729
void TButton::_TimerCallback(ulong counter)
730
{
731
    mLastBlink.second++;
732
    int months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
733
 
734
    if ((mLastBlink.year % 4) == 0)
735
        months[1] = 29;
736
 
737
    if (mLastBlink.second > 59)
738
    {
739
        mLastBlink.minute++;
740
        mLastBlink.second = 0;
741
 
742
        if (mLastBlink.minute > 59)
743
        {
744
            mLastBlink.hour++;
745
            mLastBlink.minute = 0;
746
 
747
            if (mLastBlink.hour >= 24)
748
            {
749
                mLastBlink.hour = 0;
750
                mLastBlink.weekday++;
751
                mLastBlink.day++;
752
 
753
                if (mLastBlink.weekday > 7)
754
                    mLastBlink.weekday = 0;
755
 
756
                if (mLastBlink.day > months[mLastBlink.month-1])
757
                {
758
                    mLastBlink.day = 1;
759
                    mLastBlink.month++;
760
 
761
                    if (mLastBlink.month > 12)
762
                    {
763
                        mLastBlink.year++;
764
                        mLastBlink.month = 1;
765
                    }
766
                }
767
            }
768
        }
769
    }
770
 
771
    funcTimer(mLastBlink);
772
}
773
 
21 andreas 774
void TButton::_imageRefresh(const string& url)
775
{
776
    DECL_TRACER("TButton::_imageRefresh(const string& url)");
777
 
778
    ulong parent = mHandle & 0xffff0000;
779
    getDrawOrder(sr[mActInstance]._do, (DRAW_ORDER *)&mDOrder);
780
 
781
    if (TError::isError())
782
    {
783
        TError::clear();
784
        return;
785
    }
786
 
787
    SkBitmap imgButton;
788
    imgButton.allocN32Pixels(wt, ht);
789
 
790
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
791
    {
792
        if (mDOrder[i] == ORD_ELEM_FILL)
793
        {
794
            if (!buttonFill(&imgButton, mActInstance))
795
                return;
796
        }
797
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
798
        {
799
            RESOURCE_T resource = gPrjResources->findResource(sr[mActInstance].bm);
800
 
801
            if (resource.protocol.empty())
802
            {
803
                MSG_ERROR("Resource " << sr[mActInstance].bm << " not found!");
804
                return;
805
            }
806
 
807
            try
808
            {
809
                char *content = nullptr;
810
                size_t length = 0, contentlen = 0;
811
 
812
                if ((content = gPrjResources->tcall(&length, url, resource.user, resource.password)) == nullptr)
813
                    return;
814
 
815
                contentlen = gPrjResources->getContentSize();
816
 
817
                if (content == nullptr)
818
                {
819
                    MSG_ERROR("Server returned no or invalid content!");
820
                    return;
821
                }
822
 
823
                sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
824
 
825
                if (!data)
826
                {
827
                    MSG_ERROR("Could not make an image!");
828
                    return;
829
                }
830
 
831
                SkBitmap image;
832
 
833
                if (!DecodeDataToBitmap(data, &image))
834
                {
835
                    MSG_ERROR("Error creating an image!");
836
                    return;
837
                }
838
 
839
                loadImage(&imgButton, image, mActInstance);
840
            }
841
            catch (std::exception& e)
842
            {
843
                MSG_ERROR(e.what());
844
                return;
845
            }
846
        }
847
        else if (mDOrder[i] == ORD_ELEM_ICON)
848
        {
849
            if (!buttonIcon(&imgButton, mActInstance))
850
                return;
851
        }
852
        else if (mDOrder[i] == ORD_ELEM_TEXT)
853
        {
854
            if (!buttonText(&imgButton, mActInstance))
855
                return;
856
        }
857
        else if (mDOrder[i] == ORD_ELEM_BORDER)
858
        {
859
            if (!buttonBorder(&imgButton, mActInstance))
860
                return;
861
        }
862
    }
863
 
864
    if (mGlobalOO >= 0 || sr[mActInstance].oo >= 0) // Take overall opacity into consideration
865
    {
866
        SkBitmap ooButton;
867
        int w = imgButton.width();
868
        int h = imgButton.height();
869
        ooButton.allocN32Pixels(w, h, true);
870
        SkCanvas canvas(ooButton);
871
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
872
        SkRegion region;
873
        region.setRect(irect);
874
        SkScalar oo;
875
 
876
        if (mGlobalOO >= 0 && sr[mActInstance].oo >= 0)
877
        {
878
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[mActInstance].oo);
879
            MSG_DEBUG("Set global overal opacity to " << oo);
880
        }
881
        else if (sr[mActInstance].oo >= 0)
882
        {
883
            oo = (SkScalar)sr[mActInstance].oo;
884
            MSG_DEBUG("Set overal opacity to " << oo);
885
        }
886
        else
887
        {
888
            oo = (SkScalar)mGlobalOO;
889
            MSG_DEBUG("Set global overal opacity to " << oo);
890
        }
891
 
892
        SkScalar alpha = 1.0 / 255.0 * oo;
893
        MSG_DEBUG("Calculated alpha value: " << alpha);
894
        SkPaint paint;
895
        paint.setAlphaf(alpha);
896
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
897
        canvas.drawBitmap(imgButton, 0, 0, &paint);
898
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
899
        imgButton = ooButton;
900
    }
901
 
902
    mLastImage = imgButton;
903
    size_t rowBytes = imgButton.info().minRowBytes();
904
 
905
    if (!prg_stopped && visible && _displayButton)
906
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), wt, ht, rowBytes, lt, tp);
907
}
908
 
15 andreas 909
void TButton::registerSystemButton()
910
{
911
    DECL_TRACER("TButton::registerSystemButton()");
912
 
913
    if (mSystemReg)
914
        return;
915
 
916
    // If this is a special system button, register it to receive the state
917
    if (ap == 0 && ad == 8)     // Connection status?
918
    {
919
        MSG_TRACE("Try to register button " << na << " as connection status ...");
920
 
921
        if (gAmxNet)
922
        {
923
            gAmxNet->registerNetworkState(bind(&TButton::funcNetwork, this, std::placeholders::_1), mHandle);
924
            mSystemReg = true;
925
            MSG_TRACE("Button registered");
926
        }
927
        else
928
            MSG_WARNING("Network class not initialized!");
929
 
930
    }
931
    else if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158))) // time or date
932
    {
933
        MSG_TRACE("Try to register button " << na << " as time/date ...");
934
 
935
        if (gAmxNet)
936
        {
937
            gAmxNet->registerTimer(bind(&TButton::funcTimer, this, std::placeholders::_1), mHandle);
938
            mSystemReg = true;
939
            MSG_TRACE("Button registered");
940
        }
941
        else
942
            MSG_WARNING("Network class not initialized!");
943
 
944
        if (ad >= 141 && ad <= 143 && !mTimer)
945
        {
946
            mTimer = new TTimer;
947
            mTimer->setInterval(std::chrono::milliseconds(1000));   // 1 second
948
            mTimer->registerCallback(bind(&TButton::_TimerCallback, this, std::placeholders::_1));
949
            mTimer->run();
950
        }
951
    }
952
}
953
 
954
void TButton::setHandle(ulong handle)
955
{
956
    DECL_TRACER("Button::TButton::setHandle(ulong handle)");
957
 
958
    mHandle = handle;
959
}
960
 
961
void TButton::addPushFunction(string& func, string& page)
962
{
963
    DECL_TRACER("TButton::addPushFunction(string& func, string& page)");
964
 
965
    vector<string> allFunc = { "Stan", "Prev", "Show", "Hide", "Togg", "ClearG", "ClearP", "ClearA" };
966
    vector<string>::iterator iter;
967
 
968
    for (iter = allFunc.begin(); iter != allFunc.end(); ++iter)
969
    {
970
        if (iter->compare(func) == 0)
971
        {
972
            bool found = false;
973
            vector<PUSH_FUNC_T>::iterator iterPf;
974
 
975
            for (iterPf = pushFunc.begin(); iterPf != pushFunc.end(); iterPf++)
976
            {
977
                if (iterPf->pfType.compare(func) == 0)
978
                {
979
                    iterPf->pfName = page;
980
                    found = true;
981
                    break;
982
                }
983
            }
984
 
985
            if (!found)
986
            {
987
                PUSH_FUNC_T pf;
988
                pf.pfType = func;
989
                pf.pfName = page;
990
                pushFunc.push_back(pf);
991
            }
992
        }
993
 
994
        break;
995
    }
996
}
997
 
16 andreas 998
void TButton::clearPushFunction(const string& action)
999
{
1000
    DECL_TRACER("TButton::clearPushFunction(const string& action)");
1001
 
1002
    if (pushFunc.empty())
1003
        return;
1004
 
1005
    vector<PUSH_FUNC_T>::iterator iter;
1006
 
1007
    for (iter = pushFunc.begin(); iter != pushFunc.end(); iter++)
1008
    {
1009
        if (iter->pfName.compare(action) == 0)
1010
        {
1011
            pushFunc.erase(iter);
1012
            return;
1013
        }
1014
    }
1015
}
1016
 
8 andreas 1017
void TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)
4 andreas 1018
{
8 andreas 1019
    DECL_TRACER("TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)");
4 andreas 1020
 
8 andreas 1021
    if (!order)
1022
        return;
1023
 
1024
    if (sdo.empty() || sdo.length() != 10)
4 andreas 1025
    {
8 andreas 1026
        *order     = ORD_ELEM_FILL;
1027
        *(order+1) = ORD_ELEM_BITMAP;
1028
        *(order+2) = ORD_ELEM_ICON;
1029
        *(order+3) = ORD_ELEM_TEXT;
1030
        *(order+4) = ORD_ELEM_BORDER;
1031
        return;
1032
    }
1033
 
1034
    int elems = sdo.length() / 2;
1035
 
1036
    for (int i = 0; i < elems; i++)
1037
    {
1038
        int e = atoi(sdo.substr(i * 2, 2).c_str());
1039
 
1040
        if (e < 1 || e > 5)
1041
        {
1042
            MSG_ERROR("Invalid draw order \"" << sdo << "\"!");
1043
            TError::setError();
1044
            return;
1045
        }
1046
 
1047
        *(order+i) = (DRAW_ORDER)e;
1048
    }
1049
}
1050
 
1051
bool TButton::buttonFill(SkBitmap* bm, int instance)
1052
{
1053
    DECL_TRACER("TButton::buttonFill(SkBitmap* bm, int instance)");
1054
 
1055
    if (!bm)
1056
    {
1057
        MSG_ERROR("Invalid bitmap!");
4 andreas 1058
        return false;
1059
    }
1060
 
8 andreas 1061
    SkColor color = TColor::getSkiaColor(sr[instance].cf);
1062
    MSG_DEBUG("Filling image of size " << bm->width() << "x" << bm->height() << " with color " << TColor::colorToString(color));
1063
    bm->eraseColor(color);
1064
    return true;
1065
}
1066
 
1067
bool TButton::buttonBitmap(SkBitmap* bm, int instance)
1068
{
1069
    DECL_TRACER("TButton::buttonBitmap(SkBitmap* bm, int instane)");
1070
 
21 andreas 1071
    if (sr[instance].dynamic)
1072
        return buttonDynamic(bm, instance);
1073
 
19 andreas 1074
    if (!sr[instance].mi.empty() && !sr[instance].bm.empty())       // Chameleon image?
4 andreas 1075
    {
20 andreas 1076
        MSG_TRACE("Chameleon image ...");
4 andreas 1077
        map<int, IMAGE_t>::iterator iterImages = mImages.find(sr[instance].number);
6 andreas 1078
        SkBitmap imgRed(iterImages->second.imageMi);
1079
        SkBitmap imgMask(iterImages->second.imageBm);
4 andreas 1080
 
20 andreas 1081
        SkBitmap img = drawImageButton(imgRed, imgMask, sr[instance].mi_width, sr[instance].mi_height, TColor::getSkiaColor(sr[instance].cf), TColor::getSkiaColor(sr[instance].cb));
1082
 
1083
        if (img.empty())
4 andreas 1084
        {
20 andreas 1085
            MSG_ERROR("Error creating the cameleon image \"" << sr[instance].mi << "\" / \"" << sr[instance].bm << "\"!");
1086
            TError::setError();
1087
            return false;
1088
        }
4 andreas 1089
 
20 andreas 1090
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1091
        SkImageInfo info = img.info();
1092
        SkPaint paint;
1093
        paint.setBlendMode(SkBlendMode::kSrcATop);
1094
        ctx.drawBitmap(imgMask, 0, 0, &paint);
4 andreas 1095
 
20 andreas 1096
        POSITION_t position = calcImagePosition(sr[instance].mi_width, sr[instance].mi_height, SC_BITMAP, instance);
7 andreas 1097
 
20 andreas 1098
        if (!position.valid)
1099
        {
1100
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
1101
            TError::setError();
1102
            return false;
4 andreas 1103
        }
10 andreas 1104
 
20 andreas 1105
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1106
        paint.setBlendMode(SkBlendMode::kSrc);
21 andreas 1107
 
1108
        if (sr[instance].sb == 0)
1109
            can.drawBitmap(img, position.left, position.top, &paint);
1110
        else    // Scale to fit
1111
        {
1112
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
1113
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(img);
1114
            can.drawImageRect(im, rect, &paint);
1115
        }
4 andreas 1116
    }
1117
    else if (!sr[instance].bm.empty())
1118
    {
20 andreas 1119
        MSG_TRACE("Drawing normal image ...");
4 andreas 1120
        map<int, IMAGE_t>::iterator iterImages = mImages.find(sr[instance].number);
6 andreas 1121
        SkBitmap image = iterImages->second.imageBm;
4 andreas 1122
 
6 andreas 1123
        if (image.empty())
4 andreas 1124
        {
1125
            MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
1126
            TError::setError();
1127
            return false;
1128
        }
1129
 
6 andreas 1130
        SkImageInfo info = image.info();
4 andreas 1131
        POSITION_t position = calcImagePosition(sr[instance].bm_width, sr[instance].bm_height, SC_BITMAP, instance);
1132
 
1133
        if (!position.valid)
1134
        {
1135
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
1136
            TError::setError();
1137
            return false;
1138
        }
1139
 
10 andreas 1140
        MSG_DEBUG("Putting bitmap on top of image ...");
1141
        SkPaint paint;
1142
        paint.setBlendMode(SkBlendMode::kSrc);
1143
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
21 andreas 1144
 
1145
        if (sr[instance].sb == 0)
1146
            can.drawBitmap(image, position.left, position.top, &paint);
1147
        else    // Scale to fit
1148
        {
1149
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
1150
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
1151
            can.drawImageRect(im, rect, &paint);
1152
        }
6 andreas 1153
    }
8 andreas 1154
    else
6 andreas 1155
    {
8 andreas 1156
        MSG_DEBUG("No bitmap defined.");
1157
    }
4 andreas 1158
 
8 andreas 1159
    return true;
1160
}
4 andreas 1161
 
21 andreas 1162
bool TButton::buttonDynamic(SkBitmap* bm, int instance)
1163
{
1164
    DECL_TRACER("TButton::buttonDynamic(SkBitmap* bm, int instance)");
1165
 
1166
    if (!gPrjResources)
1167
    {
1168
        MSG_ERROR("Internal error: Global resource class not initialized!");
1169
        return false;
1170
    }
1171
 
1172
    if (!sr[instance].dynamic)
1173
    {
1174
        MSG_WARNING("Button " << bi << ": \"" << na << "\" is not for remote image!");
1175
        return false;
1176
    }
1177
 
1178
    RESOURCE_T resource = gPrjResources->findResource(1, sr[instance].bm);
1179
 
1180
    if (resource.protocol.empty())
1181
    {
1182
        MSG_ERROR("Resource " << sr[instance].bm << " not found!");
1183
        return false;
1184
    }
1185
 
1186
    string path = resource.path;
1187
 
1188
    if (!resource.file.empty())
1189
        path += "/" + resource.file;
1190
 
1191
    string url = gPrjResources->makeURL(toLower(resource.protocol), resource.host, 0, path);
1192
 
1193
    try
1194
    {
1195
        MSG_TRACE("Starting thread for loading a dynamic image ...");
1196
        mThrRes = std::thread([=] { this->funcResource(&resource, url, bm, instance); });
1197
        MSG_TRACE("Thread started. Detaching ...");
1198
        mThrRes.detach();
1199
        MSG_TRACE("Thread is running and detached.");
1200
    }
1201
    catch (std::exception& e)
1202
    {
1203
        MSG_ERROR("Error starting the AmxNet thread: " << e.what());
1204
    }
1205
 
1206
    return true;
1207
}
1208
 
1209
void TButton::funcResource(const RESOURCE_T* resource, const std::string& url, SkBitmap* bm, int instance)
1210
{
1211
    DECL_TRACER("TButton::funcResource(RESOURCE_T* resource, std::string& url, SkBitmap* bm, int instance)");
1212
 
1213
    if (resource->refresh > 0 && !resource->dynamo)      // Periodically refreshing image?
1214
    {
1215
        MSG_DEBUG("Retrieving periodicaly refreshed image");
1216
 
1217
        ulong parent = (mHandle >> 16) & 0x0000ffff;
1218
 
1219
        if (!mHandle || !parent || bi <= 1)
1220
        {
1221
            MSG_ERROR("Invalid button. Can't make a dynamo image!");
1222
            return;
1223
        }
1224
 
1225
        THR_REFRESH_t *thref = _findResource(mHandle, parent, bi);
1226
        TImageRefresh *mImageRefresh = nullptr;
1227
 
1228
        if (!thref)
1229
        {
1230
            MSG_DEBUG("Creating a new refresh thread");
1231
            mImageRefresh = new TImageRefresh();
1232
            mImageRefresh->registerCallback(bind(&TButton::_imageRefresh, this, std::placeholders::_1));
1233
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
1234
            mImageRefresh->setUsername(resource->user);
1235
            mImageRefresh->setPassword(resource->password);
1236
 
1237
            if (resource->preserve)
1238
                mImageRefresh->setRunOnce();
1239
 
1240
            _addResource(mImageRefresh, mHandle, parent, bi);
1241
        }
1242
        else
1243
        {
1244
            mImageRefresh = thref->mImageRefresh;
1245
 
1246
            if (!mImageRefresh)
1247
            {
1248
                MSG_ERROR("Error creating a new refresh class!");
1249
                return;
1250
            }
1251
 
1252
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
1253
            mImageRefresh->setUsername(resource->user);
1254
            mImageRefresh->setPassword(resource->password);
1255
 
1256
            if (resource->preserve)
1257
                mImageRefresh->setRunOnce();
1258
        }
1259
 
1260
        if (!mImageRefresh->isRunning())
1261
        {
1262
            MSG_DEBUG("Starting a refresh thread.");
1263
            mImageRefresh->run(url);
1264
        }
1265
    }
1266
    else if (resource->refresh == 0 && !resource->dynamo)
1267
    {
1268
        MSG_DEBUG("Retrieving single image");
1269
 
1270
        try
1271
        {
1272
            char *content = nullptr;
1273
            size_t length = 0, contentlen = 0;
1274
 
1275
            if ((content = gPrjResources->tcall(&length, url, resource->user, resource->password)) == nullptr)
1276
                return;
1277
 
1278
            contentlen = gPrjResources->getContentSize();
1279
            MSG_DEBUG("Loaded " << contentlen << " bytes:");
1280
            sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
1281
 
1282
            if (!data)
1283
            {
1284
                MSG_ERROR("Error making image data!");
1285
                return;
1286
            }
1287
 
1288
            SkBitmap image;
1289
 
1290
            if (!DecodeDataToBitmap(data, &image))
1291
            {
1292
                MSG_ERROR("Error creating an image!");
1293
                return;
1294
            }
1295
 
1296
            loadImage(bm, image, instance);
1297
            return;
1298
        }
1299
        catch (std::exception& e)
1300
        {
1301
            MSG_ERROR(e.what());
1302
            return;
1303
        }
1304
    }
1305
    else
1306
    {
1307
        MSG_DEBUG("Retrieving a video");
1308
 
1309
        if (_playVideo && !prg_stopped)
1310
        {
1311
            ulong parent = (mHandle >> 16) & 0x0000ffff;
1312
            _playVideo(mHandle, parent, lt, tp, wt, ht, url, resource->user, resource->password);
1313
        }
1314
    }
1315
}
1316
 
1317
bool TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)
1318
{
1319
    DECL_TRACER("TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)");
1320
 
1321
    SkImageInfo info = image.info();
1322
    POSITION_t position = calcImagePosition(info.width(), info.height(), SC_BITMAP, instance);
1323
 
1324
    if (!position.valid)
1325
    {
1326
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
1327
        return false;
1328
    }
1329
 
1330
    MSG_DEBUG("Putting bitmap on top of image ...");
1331
    SkPaint paint;
1332
    paint.setBlendMode(SkBlendMode::kSrc);
1333
 
1334
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1335
 
1336
    if (sr[instance].sb == 0)
1337
        can.drawBitmap(image, position.left, position.top, &paint);
1338
    else    // Scale to fit
1339
    {
1340
        SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
1341
        sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
1342
        can.drawImageRect(im, rect, &paint);
1343
    }
1344
 
1345
    return true;
1346
}
1347
 
15 andreas 1348
bool TButton::barLevel(SkBitmap* bm, int instance, int level)
1349
{
1350
    DECL_TRACER("TButton::barLevel(SkBitmap* bm, int instance, int level)");
1351
 
20 andreas 1352
    if (!sr[0].mi.empty() && !sr[1].bm.empty())       // Chameleon image?
15 andreas 1353
    {
1354
        MSG_DEBUG("Chameleon image ...");
1355
        map<int, IMAGE_t>::iterator iterImages1 = mImages.find(sr[0].number);
1356
        map<int, IMAGE_t>::iterator iterImages2 = mImages.find(sr[1].number);
1357
        SkBitmap imgRed(iterImages1->second.imageMi);
1358
        SkBitmap imgMask(iterImages2->second.imageBm);
1359
 
20 andreas 1360
        SkBitmap img;
1361
        SkPixmap pixmapRed = imgRed.pixmap();
1362
        SkPixmap pixmapMask;
15 andreas 1363
 
20 andreas 1364
        if (!imgMask.empty())
1365
            pixmapMask = imgMask.pixmap();
15 andreas 1366
 
20 andreas 1367
        int width = sr[0].mi_width;
1368
        int height = sr[0].mi_height;
1369
        int startX = 0;
1370
        int startY = 0;
15 andreas 1371
 
20 andreas 1372
        if (dr.compare("horizontal") == 0)
1373
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
1374
        else
1375
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
1376
 
1377
        if ((!ri && dr.compare("horizontal") != 0) ||   // range inverted?
1378
            (ri && dr.compare("horizontal") == 0))
1379
        {
15 andreas 1380
            if (dr.compare("horizontal") == 0)
20 andreas 1381
                startX = sr[0].mi_width - width;
15 andreas 1382
            else
20 andreas 1383
                startY = sr[0].mi_height - height;
15 andreas 1384
 
20 andreas 1385
            width = sr[0].mi_width;
1386
            height = sr[0].mi_height;
1387
        }
15 andreas 1388
 
20 andreas 1389
        img.allocPixels(SkImageInfo::MakeN32Premul(sr[0].mi_width, sr[0].mi_height));
1390
        SkCanvas canvas(img);
1391
        SkColor col1 = TColor::getSkiaColor(sr[1].cf);
1392
        SkColor col2 = TColor::getSkiaColor(sr[1].cb);
15 andreas 1393
 
20 andreas 1394
        for (int ix = 0; ix < sr[0].mi_width; ix++)
1395
        {
1396
            for (int iy = 0; iy < sr[0].mi_height; iy++)
1397
            {
1398
                SkPaint paint;
1399
                SkColor pixel;
15 andreas 1400
 
20 andreas 1401
                if (ix >= startX && ix < width && iy >= startY && iy < height)
15 andreas 1402
                {
20 andreas 1403
                    SkColor pixelRed = pixmapRed.getColor(ix, iy);
1404
                    SkColor pixelMask;
15 andreas 1405
 
20 andreas 1406
                    if (!imgMask.empty())
1407
                        pixelMask = pixmapMask.getColor(ix, iy);
15 andreas 1408
                    else
20 andreas 1409
                        pixelMask = SK_ColorWHITE;
15 andreas 1410
 
20 andreas 1411
                    pixel = baseColor(pixelRed, pixelMask, col1, col2);
15 andreas 1412
                }
20 andreas 1413
                else
1414
                    pixel = SK_ColorTRANSPARENT;
15 andreas 1415
 
20 andreas 1416
                paint.setColor(pixel);
1417
                canvas.drawPoint(ix, iy, paint);
15 andreas 1418
            }
20 andreas 1419
        }
15 andreas 1420
 
20 andreas 1421
        if (img.empty())
1422
        {
1423
            MSG_ERROR("Error creating the cameleon image \"" << sr[0].mi << "\" / \"" << sr[0].bm << "\"!");
1424
            TError::setError();
1425
            return false;
1426
        }
15 andreas 1427
 
20 andreas 1428
        MSG_DEBUG("Putting image together ...");
1429
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1430
        SkImageInfo info = img.info();
1431
        SkPaint paint;
1432
        paint.setBlendMode(SkBlendMode::kSrcATop);
1433
        ctx.drawBitmap(imgMask, 0, 0, &paint);
15 andreas 1434
 
20 andreas 1435
        POSITION_t position = calcImagePosition(sr[0].mi_width, sr[0].mi_height, SC_BITMAP, 0);
15 andreas 1436
 
20 andreas 1437
        if (!position.valid)
1438
        {
1439
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
1440
            TError::setError();
1441
            return false;
15 andreas 1442
        }
1443
 
20 andreas 1444
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1445
        paint.setBlendMode(SkBlendMode::kSrc);
1446
        can.drawBitmap(img, position.left, position.top, &paint);
15 andreas 1447
    }
1448
    else if (!sr[1].bm.empty())
1449
    {
1450
        MSG_DEBUG("Drawing normal image ...");
1451
        map<int, IMAGE_t>::iterator iterImages = mImages.find(sr[1].number);
1452
        SkBitmap image = iterImages->second.imageBm;
1453
 
1454
        if (image.empty())
1455
        {
1456
            MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
1457
            TError::setError();
1458
            return false;
1459
        }
1460
 
1461
        int width = sr[1].mi_width;
1462
        int height = sr[1].mi_height;
1463
        int startX = 0;
1464
        int startY = 0;
1465
 
1466
        if (dr.compare("horizontal") == 0)
1467
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
1468
        else
1469
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
1470
 
1471
        if ((!ri && dr.compare("horizontal") != 0) ||   // range inverted?
1472
            (ri && dr.compare("horizontal") == 0))
1473
        {
1474
            if (dr.compare("horizontal") == 0)
1475
                startX = sr[0].mi_width - width;
1476
            else
1477
                startY = sr[0].mi_height - height;
1478
 
1479
            width = sr[0].mi_width;
1480
            height = sr[0].mi_height;
1481
        }
1482
 
1483
        MSG_DEBUG("Putting bitmap on top of image ...");
1484
        SkPaint paint;
1485
        paint.setBlendMode(SkBlendMode::kSrc);
1486
        paint.setColor(TColor::getSkiaColor(sr[1].cf));
1487
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
1488
        SkRect dst;
1489
        dst.setXYWH(startX, startY, width, height);
1490
        can.drawBitmapRect(image, dst, &paint);
1491
    }
1492
    else
1493
    {
1494
        MSG_DEBUG("No bitmap defined.");
1495
        int width = sr[1].mi_width;
1496
        int height = sr[1].mi_height;
1497
        int startX = 0;
1498
        int startY = 0;
1499
 
1500
        if (dr.compare("horizontal") == 0)
1501
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
1502
        else
1503
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
1504
 
1505
        if ((!ri && dr.compare("horizontal") != 0) ||   // range inverted?
1506
            (ri && dr.compare("horizontal") == 0))
1507
        {
1508
            if (dr.compare("horizontal") == 0)
1509
                startX = sr[0].mi_width - width;
1510
            else
1511
                startY = sr[0].mi_height - height;
1512
 
1513
            width = sr[0].mi_width;
1514
            height = sr[0].mi_height;
1515
        }
1516
 
1517
        MSG_DEBUG("Putting bitmap on top of image ...");
20 andreas 1518
//        SkBitmap image;
1519
//        image.allocN32Pixels(wt, ht);
1520
//        image.eraseColor(TColor::getSkiaColor(sr[1].cf));
15 andreas 1521
        SkPaint paint;
1522
        paint.setBlendMode(SkBlendMode::kSrc);
1523
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
20 andreas 1524
        paint.setStyle(SkPaint::kFill_Style);
1525
        paint.setAntiAlias(true);
1526
        paint.setStrokeWidth(4);
1527
        paint.setColor(TColor::getSkiaColor(sr[1].cb));
1528
 
15 andreas 1529
        SkRect dst;
1530
        dst.setXYWH(startX, startY, width, height);
20 andreas 1531
        can.drawRect(dst, paint);
15 andreas 1532
    }
1533
 
1534
    return true;
1535
}
1536
 
8 andreas 1537
bool TButton::buttonIcon(SkBitmap* bm, int instance)
1538
{
1539
    DECL_TRACER("TButton::buttonIcon(SkBitmap* bm, int instance)");
1540
 
1541
    if (sr[instance].ii <= 0)
1542
    {
1543
        MSG_TRACE("No icon defined!");
1544
        return true;
7 andreas 1545
    }
1546
 
8 andreas 1547
    MSG_DEBUG("Drawing an icon ...");
1548
 
1549
    if (!gIcons)
1550
    {
1551
        gIcons = new TIcons();
1552
 
1553
        if (TError::isError())
1554
        {
1555
            MSG_ERROR("Error initializing icons!");
1556
            return false;
1557
        }
1558
    }
1559
 
1560
    string file = gIcons->getFile(sr[instance].ii);
1561
    MSG_DEBUG("Loading icon file " << file);
1562
    sk_sp<SkData> image;
1563
    SkBitmap icon;
1564
 
1565
    if (!(image = readImage(file)))
1566
        return false;
1567
 
1568
    DecodeDataToBitmap(image, &icon);
1569
 
1570
    if (icon.empty())
1571
    {
1572
        MSG_WARNING("Could not create an icon for element " << sr[instance].ii << " on button " << bi << " (" << na << ")");
1573
        return false;
1574
    }
1575
 
1576
    SkImageInfo info = icon.info();
1577
    POSITION_t position = calcImagePosition(icon.width(), icon.height(), SC_ICON, instance);
1578
 
1579
    if (!position.valid)
1580
    {
1581
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
1582
        TError::setError();
1583
        return false;
1584
    }
1585
 
10 andreas 1586
    MSG_DEBUG("Putting Icon on top of bitmap ...");
1587
    SkPaint paint;
1588
    paint.setBlendMode(SkBlendMode::kSrcOver);
1589
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
8 andreas 1590
 
17 andreas 1591
    if (position.overflow)
1592
    {
1593
        SkIRect irect;
1594
        SkRect bdst;
1595
        SkBitmap dst;
1596
        int left = (position.left >= 0) ? 0 : position.left * -1;
1597
        int top = (position.top >= 0) ? 0 : position.top * -1;
1598
        int width = std::min(wt, info.width());
1599
        int height = std::min(ht, info.height());
1600
        irect.setXYWH(left, top, width, height);
1601
        bm->getBounds(&bdst);
1602
        can.drawBitmapRect(icon, irect, bdst, &paint);
1603
    }
1604
    else
1605
        can.drawBitmap(icon, position.left, position.top, &paint);
1606
 
8 andreas 1607
    return true;
1608
}
1609
 
1610
bool TButton::buttonText(SkBitmap* bm, int instance)
1611
{
1612
    DECL_TRACER("TButton::buttonText(SkBitmap* bm, int instance)");
1613
 
7 andreas 1614
    if (!sr[instance].te.empty() && _setText && mFonts)       // Is there a text to display?
1615
    {
8 andreas 1616
        MSG_DEBUG("Searching for font number " << sr[instance].fi << " with text " << sr[instance].te);
7 andreas 1617
        FONT_T font = mFonts->getFont(sr[instance].fi);
1618
 
1619
        if (!font.file.empty())
6 andreas 1620
        {
10 andreas 1621
            SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
7 andreas 1622
            sk_sp<SkTypeface> typeFace = mFonts->getTypeFace(sr[instance].fi);
1623
 
1624
            if (!typeFace)
1625
            {
1626
                MSG_ERROR("Error creating type face " << font.fullName);
1627
                TError::setError();
1628
                return false;
1629
            }
1630
 
19 andreas 1631
            SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
8 andreas 1632
            SkFont skFont(typeFace, fontSizePt);
19 andreas 1633
 
8 andreas 1634
            SkPaint paint;
1635
            paint.setAntiAlias(true);
1636
            SkColor color = TColor::getSkiaColor(sr[instance].ct);
1637
            paint.setColor(color);
1638
            paint.setStyle(SkPaint::kFill_Style);
1639
 
1640
            SkFontMetrics metrics;
1641
            skFont.getMetrics(&metrics);
1642
            int lines = numberLines(sr[instance].te);
1643
            MSG_DEBUG("Found " << lines << " lines.");
1644
 
1645
            if (lines > 1 || sr[instance].ww)
7 andreas 1646
            {
8 andreas 1647
                vector<string> textLines;
1648
 
1649
                if (!sr[instance].ww)
1650
                    textLines = splitLine(sr[instance].te);
1651
                else
1652
                {
1653
                    textLines = splitLine(sr[instance].te, wt, ht, skFont, paint);
1654
                    lines = textLines.size();
1655
                }
1656
 
1657
                vector<string>::iterator iter;
1658
                SkScalar Y = 1.0;
1659
 
1660
                for (iter = textLines.begin(); iter != textLines.end(); iter++)
1661
                {
19 andreas 1662
                    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
8 andreas 1663
                    SkRect rect;
1664
                    skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
1665
                    MSG_DEBUG("Triing to print line: " << *iter);
1666
                    POSITION_t position = calcImagePosition(rect.width(), rect.height()*lines, SC_TEXT, instance);
1667
 
1668
                    if (!position.valid)
1669
                    {
1670
                        MSG_ERROR("Error calculating the text position!");
1671
                        TError::setError();
1672
                        return false;
1673
                    }
1674
 
10 andreas 1675
                    SkScalar startX = (SkScalar)position.left;
1676
                    SkScalar startY = (SkScalar)position.top;
1677
 
1678
                    if (Y > 1.0)
1679
                        startY += metrics.fCapHeight; //  metrics.fDescent + metrics.fLeading;
1680
                    else
1681
                        startY += metrics.fCapHeight + metrics.fLeading; //(metrics.fAscent * -1.0);
1682
 
1683
                    MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
1684
                    MSG_DEBUG("fCapHeight: " << metrics.fCapHeight);
1685
                    MSG_DEBUG("fAscent:    " << metrics.fAscent);
1686
                    MSG_DEBUG("fDescent:   " << metrics.fDescent);
1687
                    MSG_DEBUG("fLeading:   " << metrics.fLeading);
1688
                    MSG_DEBUG("fXHeight:   " << metrics.fXHeight);
1689
                    MSG_DEBUG("x=" << startX << ", y=" << startY*Y);
19 andreas 1690
                    canvas.drawTextBlob(blob, startX, startY, paint);
8 andreas 1691
                    Y += 1.0;
1692
                }
7 andreas 1693
            }
8 andreas 1694
            else    // single line
1695
            {
19 andreas 1696
                sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(sr[instance].te.c_str(), skFont);
8 andreas 1697
                SkRect rect;
1698
                skFont.measureText(sr[instance].te.c_str(), sr[instance].te.length(), SkTextEncoding::kUTF8, &rect, &paint);
1699
                POSITION_t position = calcImagePosition(rect.width(), (rect.height() * (float)lines), SC_TEXT, instance);
7 andreas 1700
 
8 andreas 1701
                if (!position.valid)
1702
                {
1703
                    MSG_ERROR("Error calculating the text position!");
1704
                    TError::setError();
1705
                    return false;
1706
                }
1707
 
1708
                MSG_DEBUG("Printing line " << sr[instance].te);
10 andreas 1709
                SkScalar startX = (SkScalar)position.left;
1710
                SkScalar startY = (SkScalar)position.top + metrics.fCapHeight; // + metrics.fLeading; // (metrics.fAscent * -1.0);
1711
 
1712
                MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
1713
                MSG_DEBUG("fCapHeight: " << metrics.fCapHeight);
1714
                MSG_DEBUG("fAscent:    " << metrics.fAscent);
1715
                MSG_DEBUG("fDescent:   " << metrics.fDescent);
1716
                MSG_DEBUG("fLeading:   " << metrics.fLeading);
1717
                MSG_DEBUG("fXHeight:   " << metrics.fXHeight);
1718
                MSG_DEBUG("x=" << startX << ", y=" << startY);
19 andreas 1719
                canvas.drawTextBlob(blob, startX, startY, paint);
8 andreas 1720
            }
1721
        }
1722
    }
1723
 
1724
    return true;
1725
}
1726
 
1727
bool TButton::buttonBorder(SkBitmap* bm, int instance)
1728
{
1729
    DECL_TRACER("TButton::buttonBorder(SkBitmap* bm, int instance)");
1730
 
1731
    if (sr[instance].bs.empty())
1732
    {
1733
        MSG_DEBUG("No border defined.");
1734
        return true;
1735
    }
1736
 
1737
    int i = 0;
1738
 
1739
    while (sysBorders[i].id)
1740
    {
1741
        if (sr[instance].bs.compare(sysBorders[i].name) == 0)
1742
        {
1743
            MSG_DEBUG("Border " << sysBorders[i].name << " found.");
10 andreas 1744
            SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
7 andreas 1745
            SkPaint paint;
8 andreas 1746
            SkColor color = TColor::getSkiaColor(sr[instance].cb);
7 andreas 1747
 
8 andreas 1748
            paint.setColor(color);
1749
            paint.setBlendMode(SkBlendMode::kSrc);
1750
            paint.setStyle(SkPaint::kStroke_Style);
1751
            SkRRect outher, inner;
1752
 
1753
            switch (sysBorders[i].id)
1754
            {
1755
                case 1: // Single Frame
1756
                case 2: // Double Frame
1757
                case 3: // Quad Frame
1758
                    paint.setStrokeWidth(sysBorders[i].width);
1759
                    canvas.drawRect(calcRect(wt, ht, sysBorders[i].width), paint);
1760
                break;
1761
 
1762
                // FIXME: Picture frame is missing
1763
 
1764
                case 5: // Circle 15
1765
                    paint.setStrokeWidth(sysBorders[i].width);
1766
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 7.0, 7.0, paint);
1767
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1768
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 7.0, 7.0);
1769
                    paint.setColor(SK_ColorTRANSPARENT);
1770
                    canvas.drawDRRect(outher, inner, paint);
1771
                break;
1772
 
1773
                case 6: // Circle 25
1774
                    paint.setStrokeWidth(sysBorders[i].width);
1775
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 14.0, 14.0, paint);
1776
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1777
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 14.0, 14.0);
1778
                    paint.setColor(SK_ColorTRANSPARENT);
1779
                    canvas.drawDRRect(outher, inner, paint);
1780
                break;
1781
 
1782
                case 7: // Circle 35
1783
                    paint.setStrokeWidth(sysBorders[i].width);
1784
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 21.0, 21.0, paint);
1785
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1786
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 21.0, 21.0);
1787
                    paint.setColor(SK_ColorTRANSPARENT);
1788
                    canvas.drawDRRect(outher, inner, paint);
1789
                break;
1790
 
1791
                case 8: // Circle 45
1792
                    paint.setStrokeWidth(sysBorders[i].width);
1793
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 28.0, 28.0, paint);
1794
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1795
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 28.0, 28.0);
1796
                    paint.setColor(SK_ColorTRANSPARENT);
1797
                    canvas.drawDRRect(outher, inner, paint);
1798
                break;
1799
 
1800
                case 9: // Circle 55
1801
                    paint.setStrokeWidth(sysBorders[i].width);
1802
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 35.0, 35.0, paint);
1803
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1804
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 35.0, 35.0);
1805
                    paint.setColor(SK_ColorTRANSPARENT);
1806
                    canvas.drawDRRect(outher, inner, paint);
1807
                break;
1808
 
1809
                case 10: // Circle 65
1810
                    paint.setStrokeWidth(sysBorders[i].width);
1811
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 42.0, 42.0, paint);
1812
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1813
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 42.0, 42.0);
1814
                    paint.setColor(SK_ColorTRANSPARENT);
1815
                    canvas.drawDRRect(outher, inner, paint);
1816
                break;
1817
 
1818
                case 11: // Circle 75
1819
                    paint.setStrokeWidth(sysBorders[i].width);
1820
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 49.0, 49.0, paint);
1821
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1822
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 49.0, 49.0);
1823
                    paint.setColor(SK_ColorTRANSPARENT);
1824
                    canvas.drawDRRect(outher, inner, paint);
1825
                break;
1826
 
1827
                case 12: // Circle 85
1828
                    paint.setStrokeWidth(sysBorders[i].width);
1829
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 56.0, 56.0, paint);
1830
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1831
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 56.0, 56.0);
1832
                    paint.setColor(SK_ColorTRANSPARENT);
1833
                    canvas.drawDRRect(outher, inner, paint);
1834
                break;
1835
 
1836
                case 13: // Circle 95
1837
                    paint.setStrokeWidth(sysBorders[i].width);
1838
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 63.0, 63.0, paint);
1839
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1840
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 63.0, 63.0);
1841
                    paint.setColor(SK_ColorTRANSPARENT);
1842
                    canvas.drawDRRect(outher, inner, paint);
1843
                break;
1844
 
1845
                case 14: // Circle 105
1846
                    paint.setStrokeWidth(sysBorders[i].width);
1847
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 70.0, 70.0, paint);
1848
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1849
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 70.0, 70.0);
1850
                    paint.setColor(SK_ColorTRANSPARENT);
1851
                    canvas.drawDRRect(outher, inner, paint);
1852
                break;
1853
 
1854
                case 15: // Circle 115
1855
                    paint.setStrokeWidth(sysBorders[i].width);
1856
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 77.0, 77.0, paint);
1857
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1858
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 77.0, 77.0);
1859
                    paint.setColor(SK_ColorTRANSPARENT);
1860
                    canvas.drawDRRect(outher, inner, paint);
1861
                break;
1862
 
1863
                case 16: // Circle 125
1864
                    paint.setStrokeWidth(sysBorders[i].width);
1865
                    canvas.drawRoundRect(calcRect(wt, ht, sysBorders[i].width), 84.0, 84.0, paint);
1866
                    outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
1867
                    inner = SkRRect::MakeRectXY({0, 0, (SkScalar)wt, (SkScalar)ht}, 84.0, 84.0);
1868
                    paint.setColor(SK_ColorTRANSPARENT);
1869
                    canvas.drawDRRect(outher, inner, paint);
1870
                break;
1871
            }
6 andreas 1872
        }
8 andreas 1873
 
1874
        i++;
4 andreas 1875
    }
1876
 
3 andreas 1877
    return true;
1878
}
1879
 
8 andreas 1880
int TButton::numberLines(const string& str)
1881
{
1882
    DECL_TRACER("TButton::numberLines(const string& str)");
1883
 
1884
    int lines = 1;
1885
 
1886
    for (size_t i = 0; i < str.length(); i++)
1887
    {
1888
        if (str.at(i) == '\n')
1889
            lines++;
1890
    }
1891
 
1892
    return lines;
1893
}
1894
 
1895
vector<string> TButton::splitLine(const string& str)
1896
{
1897
    DECL_TRACER("TButton::splitLine(const string& str)");
1898
 
1899
    vector<string> lines;
1900
    string sl;
1901
    size_t len = str.length();
1902
 
1903
    for (size_t i = 0; i < len; i++)
1904
    {
1905
        if (str.at(i) == '\r')  // ignore bloating byte coming from brain death windows
1906
            continue;
1907
 
1908
        if (str.at(i) == '\n')
1909
        {
1910
            lines.push_back(sl);
1911
            sl.clear();
1912
            continue;
1913
        }
1914
 
1915
        sl.append(str.substr(i, 1));
1916
    }
1917
 
1918
    if (!sl.empty())
1919
        lines.push_back(sl);
1920
 
1921
    return lines;
1922
}
1923
 
1924
vector<string> TButton::splitLine(const string& str, int width, int height, SkFont& font, SkPaint& paint)
1925
{
1926
    DECL_TRACER("TButton::splitLine(const string& str, SkFont& font)");
1927
 
1928
    SkRect rect;
1929
    size_t len = str.length();
1930
    vector<string> lines;
1931
    SkScalar lnHeight = font.getSize();
1932
    int maxLines = (int)((SkScalar)height / lnHeight);
1933
 
1934
    for (size_t i = 0; i < len; i++)
1935
    {
1936
        string part = str.substr(0, i + 1);
1937
        font.measureText(part.c_str(), part.length(), SkTextEncoding::kUTF8, &rect, &paint);
1938
 
1939
        if (rect.width() > width)
1940
        {
1941
            string ln = part.substr(0, part.length() - 1);
1942
            lines.push_back(ln);
1943
            part = part.substr(part.length() - 1);
1944
 
1945
            if (lines.size() >= (size_t)maxLines)
1946
                return lines;
1947
        }
1948
 
1949
        if ((i + 1) == len)
1950
            lines.push_back(part);
1951
    }
1952
 
1953
    return lines;
1954
}
1955
 
1956
SkRect TButton::calcRect(int width, int height, int pen)
1957
{
1958
    DECL_TRACER("TButton::calcRect(int width, int height, int pen)");
1959
    SkRect rect;
1960
 
1961
    SkScalar left = (SkScalar)pen / 2.0;
1962
    SkScalar top = (SkScalar)pen / 2.0;
1963
    SkScalar w = (SkScalar)width - (SkScalar)pen / 2.0 - left;
1964
    SkScalar h = (SkScalar)height - (SkScalar)pen / 2.0 - top;
1965
    rect.setXYWH(left, top, w, h);
1966
    return rect;
1967
}
1968
 
15 andreas 1969
void TButton::runAnimation()
8 andreas 1970
{
15 andreas 1971
    DECL_TRACER("TButton::runAnimation()");
8 andreas 1972
 
15 andreas 1973
    mAniRunning = true;
1974
    int instance = 0;
1975
    int max = (int)sr.size();
1976
    ulong tm = nu * 100 + nd * 100;
1977
 
1978
    while (mAniRunning)
1979
    {
1980
        if (!drawButton(instance))
1981
            break;
1982
 
1983
        instance++;
1984
 
1985
        if (instance >= max)
1986
            instance = 0;
1987
 
1988
        std::this_thread::sleep_for(std::chrono::milliseconds(tm));
1989
    }
1990
 
1991
    mAniRunning = false;
1992
}
1993
 
1994
bool TButton::drawButtonMultistateAni()
1995
{
1996
    DECL_TRACER("TButton::drawButtonMultistate(int instance, bool show)");
1997
 
1998
    if (mAniRunning || mThrAni.joinable())
1999
    {
2000
        MSG_INFO("Animation is already running!");
2001
        return true;
2002
    }
2003
 
2004
    try
2005
    {
2006
        mThrAni = thread([=] { runAnimation(); });
2007
        mThrAni.detach();
2008
    }
2009
    catch (exception& e)
2010
    {
2011
        MSG_ERROR("Error starting the button animation thread: " << e.what());
2012
        return false;
2013
    }
2014
 
2015
    return true;
2016
}
2017
 
2018
bool TButton::drawButton(int instance, bool show)
2019
{
2020
    mutex_button.lock();
2021
    DECL_TRACER("TButton::drawButton(int instance, bool show)");
2022
 
8 andreas 2023
    if ((size_t)instance >= sr.size() || instance < 0)
2024
    {
2025
        MSG_ERROR("Instance " << instance << " is out of bounds!");
2026
        TError::setError();
15 andreas 2027
        mutex_button.unlock();
8 andreas 2028
        return false;
2029
    }
2030
 
14 andreas 2031
    if (!visible || instance != mActInstance || !_displayButton)
2032
    {
15 andreas 2033
        bool db = (_displayButton != nullptr);
14 andreas 2034
        MSG_INFO("Button " << bi << ", \"" << na << "\" at instance " << instance << " is not to draw!");
15 andreas 2035
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
2036
        mutex_button.unlock();
14 andreas 2037
        return true;
2038
    }
2039
 
8 andreas 2040
    ulong parent = mHandle & 0xffff0000;
2041
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
2042
 
2043
    if (TError::isError())
15 andreas 2044
    {
2045
        mutex_button.unlock();
8 andreas 2046
        return false;
15 andreas 2047
    }
8 andreas 2048
 
2049
    SkBitmap imgButton;
10 andreas 2050
    imgButton.allocN32Pixels(wt, ht);
8 andreas 2051
 
2052
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
2053
    {
2054
        if (mDOrder[i] == ORD_ELEM_FILL)
2055
        {
2056
            if (!buttonFill(&imgButton, instance))
15 andreas 2057
            {
2058
                mutex_button.unlock();
8 andreas 2059
                return false;
15 andreas 2060
            }
8 andreas 2061
        }
2062
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
2063
        {
21 andreas 2064
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
15 andreas 2065
            {
2066
                mutex_button.unlock();
8 andreas 2067
                return false;
15 andreas 2068
            }
21 andreas 2069
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance))
2070
            {
2071
                mutex_button.unlock();
2072
                return false;
2073
            }
8 andreas 2074
        }
2075
        else if (mDOrder[i] == ORD_ELEM_ICON)
2076
        {
2077
            if (!buttonIcon(&imgButton, instance))
15 andreas 2078
            {
2079
                mutex_button.unlock();
8 andreas 2080
                return false;
15 andreas 2081
            }
8 andreas 2082
        }
2083
        else if (mDOrder[i] == ORD_ELEM_TEXT)
2084
        {
2085
            if (!buttonText(&imgButton, instance))
15 andreas 2086
            {
2087
                mutex_button.unlock();
8 andreas 2088
                return false;
15 andreas 2089
            }
8 andreas 2090
        }
2091
        else if (mDOrder[i] == ORD_ELEM_BORDER)
2092
        {
2093
            if (!buttonBorder(&imgButton, instance))
15 andreas 2094
            {
2095
                mutex_button.unlock();
8 andreas 2096
                return false;
15 andreas 2097
            }
8 andreas 2098
        }
2099
    }
2100
 
10 andreas 2101
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
2102
    {
2103
        SkBitmap ooButton;
2104
        int w = imgButton.width();
2105
        int h = imgButton.height();
2106
        ooButton.allocN32Pixels(w, h, true);
2107
        SkCanvas canvas(ooButton);
2108
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
2109
        SkRegion region;
2110
        region.setRect(irect);
2111
        SkScalar oo;
2112
 
2113
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
2114
        {
2115
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
2116
            MSG_DEBUG("Set global overal opacity to " << oo);
2117
        }
2118
        else if (sr[instance].oo >= 0)
2119
        {
2120
            oo = (SkScalar)sr[instance].oo;
2121
            MSG_DEBUG("Set overal opacity to " << oo);
2122
        }
2123
        else
2124
        {
2125
            oo = (SkScalar)mGlobalOO;
2126
            MSG_DEBUG("Set global overal opacity to " << oo);
2127
        }
2128
 
2129
        SkScalar alpha = 1.0 / 255.0 * oo;
2130
        MSG_DEBUG("Calculated alpha value: " << alpha);
2131
        SkPaint paint;
2132
        paint.setAlphaf(alpha);
2133
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
2134
        canvas.drawBitmap(imgButton, 0, 0, &paint);
2135
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
2136
        imgButton = ooButton;
2137
    }
2138
 
15 andreas 2139
    mLastImage = imgButton;
8 andreas 2140
    size_t rowBytes = imgButton.info().minRowBytes();
2141
 
21 andreas 2142
    if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
8 andreas 2143
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), wt, ht, rowBytes, lt, tp);
2144
 
15 andreas 2145
    mutex_button.unlock();
8 andreas 2146
    return true;
2147
}
2148
 
15 andreas 2149
bool TButton::drawBargraph(int instance, int level, bool show)
2150
{
2151
    mutex_bargraph.lock();
2152
    DECL_TRACER("TButton::drawBargraph(int instance, bool show)");
2153
 
2154
    if ((size_t)instance >= sr.size() || instance < 0)
2155
    {
2156
        MSG_ERROR("Instance " << instance << " is out of bounds!");
2157
        TError::setError();
2158
        mutex_bargraph.unlock();
2159
        return false;
2160
    }
2161
 
2162
    if (level < rl)
2163
        mLastLevel = rl;
2164
    else if (level > rh)
2165
        mLastLevel = rh;
2166
    else
2167
        mLastLevel = level;
2168
 
2169
    if (!visible || instance != mActInstance || !_displayButton)
2170
    {
2171
        bool db = (_displayButton != nullptr);
2172
        MSG_INFO("Bargraph " << bi << ", \"" << na << "\" at instance " << instance << " with level " << mLastLevel << " is not to draw!");
2173
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
2174
        mutex_bargraph.unlock();
2175
        return true;
2176
    }
2177
 
2178
    ulong parent = mHandle & 0xffff0000;
20 andreas 2179
 
2180
    if (type == BARGRAPH)
2181
        getDrawOrder(sr[1]._do, (DRAW_ORDER *)&mDOrder);
2182
    else
2183
        getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
2184
 
15 andreas 2185
    if (TError::isError())
2186
    {
2187
        mutex_bargraph.unlock();
2188
        return false;
2189
    }
2190
 
2191
    SkBitmap imgButton;
2192
    imgButton.allocN32Pixels(wt, ht);
2193
 
2194
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
2195
    {
2196
        if (mDOrder[i] == ORD_ELEM_FILL)
2197
        {
2198
            if (!buttonFill(&imgButton, instance))
2199
            {
2200
                mutex_bargraph.unlock();
2201
                return false;
2202
            }
2203
        }
2204
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
2205
        {
2206
            if (!barLevel(&imgButton, instance, mLastLevel))
2207
            {
2208
                mutex_bargraph.unlock();
2209
                return false;
2210
            }
2211
        }
2212
        else if (mDOrder[i] == ORD_ELEM_ICON)
2213
        {
2214
            if (!buttonIcon(&imgButton, instance))
2215
            {
2216
                mutex_bargraph.unlock();
2217
                return false;
2218
            }
2219
        }
2220
        else if (mDOrder[i] == ORD_ELEM_TEXT)
2221
        {
2222
            if (!buttonText(&imgButton, instance))
2223
            {
2224
                mutex_bargraph.unlock();
2225
                return false;
2226
            }
2227
        }
2228
        else if (mDOrder[i] == ORD_ELEM_BORDER)
2229
        {
2230
            if (!buttonBorder(&imgButton, instance))
2231
            {
2232
                mutex_bargraph.unlock();
2233
                return false;
2234
            }
2235
        }
2236
    }
2237
 
2238
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
2239
    {
2240
        SkBitmap ooButton;
2241
        int w = imgButton.width();
2242
        int h = imgButton.height();
2243
        ooButton.allocN32Pixels(w, h, true);
2244
        SkCanvas canvas(ooButton);
2245
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
2246
        SkRegion region;
2247
        region.setRect(irect);
2248
        SkScalar oo;
2249
 
2250
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
2251
        {
2252
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
2253
            MSG_DEBUG("Set global overal opacity to " << oo);
2254
        }
2255
        else if (sr[instance].oo >= 0)
2256
        {
2257
            oo = (SkScalar)sr[instance].oo;
2258
            MSG_DEBUG("Set overal opacity to " << oo);
2259
        }
2260
        else
2261
        {
2262
            oo = (SkScalar)mGlobalOO;
2263
            MSG_DEBUG("Set global overal opacity to " << oo);
2264
        }
2265
 
2266
        SkScalar alpha = 1.0 / 255.0 * oo;
2267
        MSG_DEBUG("Calculated alpha value: " << alpha);
2268
        SkPaint paint;
2269
        paint.setAlphaf(alpha);
2270
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
2271
        canvas.drawBitmap(imgButton, 0, 0, &paint);
2272
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
2273
        imgButton = ooButton;
2274
    }
2275
 
2276
    mLastImage = imgButton;
2277
    size_t rowBytes = imgButton.info().minRowBytes();
2278
 
21 andreas 2279
    if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
15 andreas 2280
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), wt, ht, rowBytes, lt, tp);
2281
 
2282
    mutex_bargraph.unlock();
2283
    return true;
2284
}
2285
 
4 andreas 2286
POSITION_t TButton::calcImagePosition(int width, int height, CENTER_CODE cc, int number)
2287
{
2288
    DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
2289
 
2290
    SR_T act_sr;
2291
    POSITION_t position;
8 andreas 2292
    int ix, iy;
4 andreas 2293
 
2294
    if (sr.size() == 0)
2295
        return position;
2296
 
2297
    if (number <= 0)
2298
        act_sr = sr.at(0);
2299
    else if ((size_t)number < sr.size())
2300
        act_sr = sr.at(number);
2301
    else
2302
        return position;
2303
 
2304
    int border_size = getBorderSize(act_sr.bs);
8 andreas 2305
    int code, border = border_size;
10 andreas 2306
    string dbgCC;
17 andreas 2307
    int rwt = 0, rht = 0;
4 andreas 2308
 
2309
    switch (cc)
3 andreas 2310
    {
8 andreas 2311
        case SC_ICON:
2312
            code = act_sr.ji;
2313
            ix = act_sr.ix;
2314
            iy = act_sr.iy;
2315
            border = border_size = 0;
10 andreas 2316
            dbgCC = "ICON";
17 andreas 2317
            rwt = width;
2318
            rht = height;
8 andreas 2319
        break;
2320
 
2321
        case SC_BITMAP:
2322
            code = act_sr.jb;
10 andreas 2323
            ix = act_sr.bx;
2324
            iy = act_sr.by;
2325
            dbgCC = "BITMAP";
17 andreas 2326
            rwt = std::min(wt - border * 2, width);
2327
            rht = std::min(ht - border_size * 2, height);
8 andreas 2328
        break;
2329
 
2330
        case SC_TEXT:
2331
            code = act_sr.jt;
2332
            ix = act_sr.tx;
2333
            iy = act_sr.ty;
10 andreas 2334
            dbgCC = "TEXT";
2335
            border += 4;
17 andreas 2336
            rwt = std::min(wt - border * 2, width);
2337
            rht = std::min(ht - border_size * 2, height);
8 andreas 2338
        break;
3 andreas 2339
    }
4 andreas 2340
 
17 andreas 2341
    if (width > rwt || height > rht)
2342
        position.overflow = true;
8 andreas 2343
 
4 andreas 2344
    switch (code)
2345
    {
2346
        case 0: // absolute position
8 andreas 2347
            position.left = ix;
2348
            position.top = iy;
10 andreas 2349
            position.width = rwt;
2350
            position.height = rht;
4 andreas 2351
        break;
2352
 
2353
        case 1: // top, left
10 andreas 2354
            if (cc == SC_TEXT)
2355
                position.left = border;
2356
 
2357
            position.width = rwt;
2358
            position.height = rht;
4 andreas 2359
        break;
2360
 
2361
        case 2: // center, top
10 andreas 2362
            position.left = (wt - rwt) / 2;
2363
            position.height = rht;
2364
            position.width = rwt;
4 andreas 2365
        break;
2366
 
2367
        case 3: // right, top
10 andreas 2368
            position.left = wt - rwt;
19 andreas 2369
 
2370
            if (cc == SC_TEXT)
2371
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
2372
 
10 andreas 2373
            position.width = rwt;
2374
            position.height = rht;
4 andreas 2375
        break;
2376
 
2377
        case 4: // left, middle
10 andreas 2378
            if (cc == SC_TEXT)
2379
                position.left = border;
2380
 
2381
            position.top = (ht - rht) / 2;
2382
            position.width = rwt;
2383
            position.height = rht;
4 andreas 2384
        break;
2385
 
2386
        case 6: // right, middle
10 andreas 2387
            position.left = wt - rwt;
19 andreas 2388
 
2389
            if (cc == SC_TEXT)
2390
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
2391
 
10 andreas 2392
            position.top = (ht - rht) / 2;
2393
            position.width = rwt;
2394
            position.height = rht;
4 andreas 2395
        break;
2396
 
2397
        case 7: // left, bottom
10 andreas 2398
            if (cc == SC_TEXT)
2399
                position.left = border_size;
2400
 
2401
            position.top = ht - rht;
2402
            position.width = rwt;
2403
            position.height = rht;
4 andreas 2404
        break;
2405
 
2406
        case 8: // center, bottom
10 andreas 2407
            position.left = (wt - rwt) / 2;
2408
            position.top = ht - rht;
2409
            position.width = rwt;
2410
            position.height = rht;
4 andreas 2411
        break;
2412
 
2413
        case 9: // right, bottom
10 andreas 2414
            position.left = wt - rwt;
19 andreas 2415
 
2416
            if (cc == SC_TEXT)
2417
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
2418
 
10 andreas 2419
            position.top = ht - rht;
4 andreas 2420
        break;
2421
 
2422
        default: // center, middle
10 andreas 2423
            position.left = (wt - rwt) / 2;
2424
            position.top = (ht - rht) / 2;
2425
            position.width = rwt;
2426
            position.height = rht;
4 andreas 2427
    }
2428
 
17 andreas 2429
    MSG_DEBUG("Type: " << dbgCC << ", PosType=" << code << ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width << ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
4 andreas 2430
    position.valid = true;
2431
    return position;
2432
}
2433
 
2434
int TButton::getBorderSize(const std::string& name)
2435
{
2436
    DECL_TRACER("TButton::getBorderSize(const std::string& name)");
2437
 
2438
    for (int i = 0; sysBorders[i].name != nullptr; i++)
2439
    {
2440
        if (name.compare(sysBorders[i].name) == 0)
8 andreas 2441
        {
2442
            MSG_DEBUG("Border size: " << sysBorders[i].width);
4 andreas 2443
            return sysBorders[i].width;
8 andreas 2444
        }
4 andreas 2445
    }
2446
 
8 andreas 2447
    MSG_DEBUG("Border size: 0");
4 andreas 2448
    return 0;
2449
}
2450
 
2451
void TButton::calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)
2452
{
2453
    DECL_TRACER("TButton::clacImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)");
2454
 
2455
    int spX = btWidth - (btFrame * 2);
2456
    int spY = btHeight - (btFrame * 2);
2457
 
2458
    if (imWidth <= spX && imHeight <= spY)
2459
    {
2460
        *realX = imWidth;
2461
        *realY = imHeight;
2462
        return;
2463
    }
2464
 
2465
    int oversizeX = 0, oversizeY = 0;
2466
 
2467
    if (imWidth > spX)
2468
        oversizeX = imWidth - spX;
2469
 
2470
    if (imHeight > spY)
2471
        oversizeY = imHeight - spY;
2472
 
2473
    double percent = 0.0;
2474
 
2475
    if (oversizeX > oversizeY)
2476
        percent = 100.0 / (double)imWidth * (double)spX;
3 andreas 2477
    else
4 andreas 2478
        percent = 100.0 / (double)imHeight * (double)spY;
2479
 
2480
    *realX = (int)(percent / 100.0 * (double)imWidth);
2481
    *realY = (int)(percent / 100.0 * (double)imHeight);
2482
}
2483
 
10 andreas 2484
SkBitmap TButton::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
4 andreas 2485
{
6 andreas 2486
    DECL_TRACER("TButton::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
4 andreas 2487
 
6 andreas 2488
    SkPixmap pixmapRed = imgRed.pixmap();
7 andreas 2489
    SkPixmap pixmapMask;
4 andreas 2490
 
7 andreas 2491
    if (!imgMask.empty())
2492
        pixmapMask = imgMask.pixmap();
4 andreas 2493
 
7 andreas 2494
    SkBitmap maskBm;
2495
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
2496
    SkCanvas canvas(maskBm);
3 andreas 2497
 
4 andreas 2498
    for (int ix = 0; ix < width; ix++)
2499
    {
2500
        for (int iy = 0; iy < height; iy++)
3 andreas 2501
        {
4 andreas 2502
            SkColor pixelRed = pixmapRed.getColor(ix, iy);
7 andreas 2503
            SkColor pixelMask;
3 andreas 2504
 
7 andreas 2505
            if (!imgMask.empty())
2506
                pixelMask = pixmapMask.getColor(ix, iy);
2507
            else
2508
                pixelMask = SK_ColorWHITE;
3 andreas 2509
 
10 andreas 2510
            SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
20 andreas 2511
            uint32_t alpha = SkColorGetA(pixel);
7 andreas 2512
            SkPaint paint;
20 andreas 2513
 
2514
            if (alpha == 0)
2515
                pixel = pixelMask;
2516
 
4 andreas 2517
            paint.setColor(pixel);
7 andreas 2518
            canvas.drawPoint(ix, iy, paint);
3 andreas 2519
        }
2520
    }
2521
 
7 andreas 2522
    return maskBm;
3 andreas 2523
}
6 andreas 2524
 
2525
void TButton::show()
2526
{
2527
    DECL_TRACER("TButton::show()");
2528
 
15 andreas 2529
    visible = true;
2530
    makeElement();
6 andreas 2531
 
15 andreas 2532
    if (isSystemButton() && !mSystemReg)
2533
        registerSystemButton();
2534
}
2535
 
2536
void TButton::showLastButton()
2537
{
2538
    DECL_TRACER("TButton::showLastButton()");
2539
 
2540
    if (mLastImage.empty())
2541
        return;
2542
 
21 andreas 2543
    if (!prg_stopped && visible && _displayButton)
6 andreas 2544
    {
15 andreas 2545
        ulong parent = mHandle & 0xffff0000;
2546
        size_t rowBytes = mLastImage.info().minRowBytes();
2547
        _displayButton(mHandle, parent, (unsigned char *)mLastImage.getPixels(), wt, ht, rowBytes, lt, tp);
2548
    }
2549
}
2550
 
2551
void TButton::hide(bool total)
2552
{
2553
    DECL_TRACER("TButton::hide()");
2554
 
2555
    if (type == MULTISTATE_GENERAL && ar == 1)
2556
        mAniRunning = false;
2557
 
21 andreas 2558
    if (!prg_stopped && total)
15 andreas 2559
    {
2560
        SkBitmap imgButton;
2561
        imgButton.allocN32Pixels(wt, ht);
2562
        imgButton.eraseColor(SK_ColorTRANSPARENT);
2563
        ulong parent = mHandle & 0xffff0000;
2564
        size_t rowBytes = imgButton.info().minRowBytes();
2565
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), wt, ht, rowBytes, lt, tp);
2566
    }
2567
 
2568
    visible = false;
2569
}
2570
 
2571
bool TButton::isClickable()
2572
{
2573
    DECL_TRACER("TButton::isClickable()");
2574
 
16 andreas 2575
    if (mEnabled && ((cp != 0 && ch != 0) || !op.empty() || !pushFunc.empty()) && hs.compare("passThru") != 0)
15 andreas 2576
        return true;
2577
 
2578
    return false;
2579
}
2580
 
2581
/**
2582
 * Handling of system button "connection state". It consists of 12 states
2583
 * indicating the network status. The states have the following meaning:
2584
 *
2585
 * 0      Diconnected (never was connected before since startup)
2586
 * 1 - 6  Connected (blink may be shown with dark and light green)
2587
 * 7, 8   Disconnected (timeout or loss of connection)
2588
 * 9 - 11 Connection in progress
2589
 */
2590
void TButton::funcNetwork(int state)
2591
{
2592
    mutex_sysdraw.lock();
2593
    DECL_TRACER("TButton::funcNetwork(int state)");
2594
 
2595
    mLastLevel = state;
2596
    mActInstance = state;
2597
 
2598
    if (visible)
2599
        makeElement(state);
2600
 
2601
    mutex_sysdraw.unlock();
2602
}
2603
 
2604
/**
2605
 * Handling the timer event from the controller. This comes usualy every
2606
 * 20th part of a second (1 second / 20)
2607
 */
2608
void TButton::funcTimer(const amx::ANET_BLINK& blink)
2609
{
2610
    mutex_sysdraw.lock();
2611
    DECL_TRACER("TButton::funcTimer(const amx::ANET_BLINK& blink)");
2612
 
2613
    string tm;
2614
    std::stringstream sstr;
2615
 
2616
    switch (ad)
2617
    {
2618
        case 141:   // Standard time
2619
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
2620
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << ":"
2621
                 << std::setw(2) << std::setfill('0') << (int)blink.second;
2622
            mLastBlink = blink;
2623
        break;
2624
 
2625
        case 142:   // Time AM/PM
6 andreas 2626
        {
15 andreas 2627
            int hour = (blink.hour > 12) ? (blink.hour - 12) : blink.hour;
2628
            sstr << std::setw(2) << std::setfill('0') << hour << ":"
2629
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << " ";
2630
 
2631
            if (blink.hour <= 12)
2632
                sstr << "AM";
2633
            else
2634
                sstr << "PM";
2635
 
2636
            mLastBlink = blink;
6 andreas 2637
        }
15 andreas 2638
        break;
6 andreas 2639
 
15 andreas 2640
        case 143:   // Time 24 hours
2641
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
2642
                 << std::setw(2) << std::setfill('0') << (int)blink.minute;
2643
            mLastBlink = blink;
2644
        break;
2645
 
2646
        case 151:   // Weekday
2647
            switch (blink.weekday)
2648
            {
2649
                case 0: sstr << "Monday"; break;
2650
                case 1: sstr << "Tuesday"; break;
2651
                case 2: sstr << "Wednesday"; break;
2652
                case 3: sstr << "Thursday"; break;
2653
                case 4: sstr << "Friday"; break;
2654
                case 5: sstr << "Saturday"; break;
2655
                case 6: sstr << "Sunday"; break;
2656
            }
2657
        break;
2658
 
2659
        case 152:   // Date mm/dd
2660
            sstr << (int)blink.month << "/" << (int)blink.day;
2661
        break;
2662
 
2663
        case 153:   // Date dd/mm
2664
            sstr << (int)blink.day << "/" << (int)blink.month;
2665
        break;
2666
 
2667
        case 154:   // Date mm/dd/yyyy
2668
            sstr << (int)blink.month << "/" << (int)blink.day << "/" << (int)blink.year;
2669
        break;
2670
 
2671
        case 155:   // Date dd/mm/yyyy
2672
            sstr << blink.day << "/" << blink.month << "/" << blink.year;
2673
        break;
2674
 
2675
        case 156:   // Date month dd/yyyy
2676
            switch (blink.month)
2677
            {
2678
                case 1:  sstr << "January"; break;
2679
                case 2:  sstr << "February"; break;
2680
                case 3:  sstr << "March"; break;
2681
                case 4:  sstr << "April"; break;
2682
                case 5:  sstr << "May"; break;
2683
                case 6:  sstr << "June"; break;
2684
                case 7:  sstr << "July"; break;
2685
                case 8:  sstr << "August"; break;
2686
                case 9:  sstr << "September"; break;
2687
                case 10:  sstr << "October"; break;
2688
                case 11:  sstr << "November"; break;
2689
                case 12:  sstr << "December"; break;
2690
            }
2691
 
2692
            sstr << " " << (int)blink.day << "/" << (int)blink.year;
2693
        break;
2694
 
2695
        case 157:   // Date dd month yyyy
2696
            sstr << (int)blink.day;
2697
 
2698
            switch (blink.month)
2699
            {
2700
                case 1:  sstr << "January"; break;
2701
                case 2:  sstr << "February"; break;
2702
                case 3:  sstr << "March"; break;
2703
                case 4:  sstr << "April"; break;
2704
                case 5:  sstr << "May"; break;
2705
                case 6:  sstr << "June"; break;
2706
                case 7:  sstr << "July"; break;
2707
                case 8:  sstr << "August"; break;
2708
                case 9:  sstr << "September"; break;
2709
                case 10:  sstr << "October"; break;
2710
                case 11:  sstr << "November"; break;
2711
                case 12:  sstr << "December"; break;
2712
            }
2713
 
2714
            sstr << " " << (int)blink.year;
2715
        break;
2716
 
2717
        case 158:   // Date yyyy-mm-dd
2718
            sstr << (int)blink.year << "-" << (int)blink.month << "-" << (int)blink.day;
2719
        break;
6 andreas 2720
    }
15 andreas 2721
 
2722
    vector<SR_T>::iterator iter;
2723
    tm = sstr.str();
2724
 
2725
    for (iter = sr.begin(); iter != sr.end(); iter++)
2726
        iter->te = tm;
2727
 
2728
    if (visible)
2729
        makeElement(mActInstance);
2730
 
2731
    mutex_sysdraw.unlock();
6 andreas 2732
}
7 andreas 2733
 
15 andreas 2734
bool TButton::isPixelTransparent(int x, int y)
2735
{
2736
    DECL_TRACER("TButton::isPixelTransparent(int x, int y)");
2737
 
2738
    if (mLastImage.empty())
2739
    {
2740
        MSG_ERROR("Internal error: No image for button available!");
2741
        return true;
2742
    }
2743
 
2744
    float alpha = mLastImage.getAlphaf(x, y);
2745
 
2746
    if (alpha != 0.0)
2747
        return false;
2748
 
2749
    return true;
2750
}
2751
 
2752
/**
2753
 * This button got the click because it matches the coordinates of a mouse
2754
 * click. It checkes whether it is clickable or not. If it is clickable, it
2755
 * depends on the type of element what happens.
2756
 */
2757
bool TButton::doClick(int x, int y, bool pressed)
2758
{
11 andreas 2759
    DECL_TRACER("TButton::doClick(bool pressed)");
14 andreas 2760
    amx::ANET_SEND scmd;
15 andreas 2761
    int instance = 0;
11 andreas 2762
 
15 andreas 2763
    if (!isClickable())
2764
        return false;
2765
 
11 andreas 2766
    if (type == GENERAL)
2767
    {
2768
        if (fb == FB_MOMENTARY)
2769
        {
2770
            if (pressed)
15 andreas 2771
                instance = 1;
14 andreas 2772
            else
15 andreas 2773
                instance = 0;
2774
 
2775
            mActInstance = instance;
2776
            // we ignore the state of the method, because it doesn't matter
2777
            // for the message to the controller.
2778
            drawButton(instance, false);
2779
            // If there is nothing in "hs", then it depends on the pixel of the
2780
            // layer. Only if the pixel the coordinates point to are not
2781
            // transparent, the button takes the click.
2782
            if (hs.empty() && isPixelTransparent(x, y))
2783
                return false;
2784
 
2785
            if (pushFunc.empty())   // Don't draw the button if it has a push function defined
2786
                showLastButton();
2787
            else
14 andreas 2788
                mActInstance = 0;
15 andreas 2789
        }
2790
        else if (fb == FB_CHANNEL || fb == FB_NONE)
2791
        {
2792
            if (pressed)
2793
                instance = 1;
2794
            else
2795
                instance = 0;
14 andreas 2796
 
15 andreas 2797
            // we ignore the state of the method, because it doesn't matter
2798
            // for the message to the controller.
2799
            drawButton(instance, false);
2800
            // If there is nothing in "hs", then it depends on the pixel of the
2801
            // layer. Only if the pixel the coordinates point to are not
2802
            // transparent, the button takes the click.
2803
            if (hs.empty() && isPixelTransparent(x, y))
2804
                return false;
14 andreas 2805
        }
15 andreas 2806
        else if (fb == FB_INV_CHANNEL)
2807
        {
2808
            if (pressed)
2809
                instance = 0;
2810
            else
2811
                instance = 1;
14 andreas 2812
 
15 andreas 2813
            // we ignore the state of the method, because it doesn't matter
2814
            // for the message to the controller.
2815
            drawButton(instance, false);
2816
            // If there is nothing in "hs", then it depends on the pixel of the
2817
            // layer. Only if the pixel the coordinates point to are not
2818
            // transparent, the button takes the click.
2819
            if (hs.empty() && isPixelTransparent(x, y))
2820
                return false;
2821
        }
2822
        else if (fb == FB_ALWAYS_ON)
14 andreas 2823
        {
15 andreas 2824
            instance = 1;
2825
            mActInstance = 1;
2826
            // we ignore the state of the method, because it doesn't matter
2827
            // for the message to the controller.
2828
            drawButton(instance, false);
2829
            // If there is nothing in "hs", then it depends on the pixel of the
2830
            // layer. Only if the pixel the coordinates point to are not
2831
            // transparent, the button takes the click.
2832
            if (hs.empty() && isPixelTransparent(x, y))
2833
                return false;
2834
        }
2835
 
2836
        if ((cp && ch) || !op.empty())
2837
        {
14 andreas 2838
            scmd.device = TConfig::getChannel();
2839
            scmd.port = cp;
2840
            scmd.channel = ch;
2841
 
15 andreas 2842
            if (op.empty())
2843
            {
2844
                if (instance)
2845
                    scmd.MC = 0x0084;
2846
                else
2847
                    scmd.MC = 0x0085;
2848
            }
14 andreas 2849
            else
15 andreas 2850
            {
2851
                scmd.MC = 0x008b;
2852
                scmd.msg = op;
2853
            }
14 andreas 2854
 
2855
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
2856
            MSG_DEBUG("Sending to device <" << scmd.device << ":" << scmd.port << ":0> channel " << scmd.channel << " value 0x" << std::setw(2) << std::setfill('0') << std::hex << scmd.MC << " (" << (pressed?"PUSH":"RELEASE") << ")");
2857
 
2858
            if (gAmxNet)
15 andreas 2859
            {
2860
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
2861
                    gAmxNet->sendCommand(scmd);
2862
            }
14 andreas 2863
            else
2864
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
2865
        }
2866
    }
15 andreas 2867
    else if (type == MULTISTATE_GENERAL)
2868
    {
2869
        if ((cp && ch) || !op.empty())
2870
        {
2871
            scmd.device = TConfig::getChannel();
2872
            scmd.port = cp;
2873
            scmd.channel = ch;
14 andreas 2874
 
15 andreas 2875
            if (op.empty())
2876
            {
2877
                if (pressed || fb == FB_ALWAYS_ON)
2878
                    scmd.MC = 0x0084;
2879
                else
2880
                    scmd.MC = 0x0085;
2881
            }
2882
            else
2883
            {
2884
                scmd.MC = 0x008b;
2885
                scmd.msg = op;
2886
            }
2887
 
2888
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
2889
            MSG_DEBUG("Sending to device <" << scmd.device << ":" << scmd.port << ":0> channel " << scmd.channel << " value 0x" << std::setw(2) << std::setfill('0') << std::hex << scmd.MC << " (" << (pressed?"PUSH":"RELEASE") << ")");
2890
 
2891
            if (gAmxNet)
2892
            {
2893
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
2894
                    gAmxNet->sendCommand(scmd);
2895
            }
2896
            else
2897
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
2898
        }
2899
    }
2900
 
2901
    /* FIXME: Move the following to class TPageManager!
2902
     *        To do that, the preconditions are to be implemented. It must be
2903
     *        possible to find the button and get access to the credentials
2904
     *        of it.
2905
     */
14 andreas 2906
    if (!pushFunc.empty() && pressed)
2907
    {
2908
        vector<PUSH_FUNC_T>::iterator iter;
2909
 
2910
        for (iter = pushFunc.begin(); iter != pushFunc.end(); iter++)
2911
        {
15 andreas 2912
            if (iter->pfType == "sShow")            // show popup
11 andreas 2913
            {
14 andreas 2914
                if (gPageManager)
2915
                    gPageManager->showSubPage(iter->pfName);
11 andreas 2916
            }
15 andreas 2917
            else if (iter->pfType == "sHide")       // hide popup
11 andreas 2918
            {
14 andreas 2919
                if (gPageManager)
2920
                    gPageManager->hideSubPage(iter->pfName);
11 andreas 2921
            }
15 andreas 2922
            else if (iter->pfType == "scGroup")     // hide group
14 andreas 2923
            {
2924
                if (gPageManager)
2925
                    gPageManager->closeGroup(iter->pfName);
2926
            }
15 andreas 2927
            else if (iter->pfType == "Stan")        // Flip to standard page
14 andreas 2928
            {
2929
                if (gPageManager)
2930
                {
15 andreas 2931
                    TPage *page = gPageManager->getActualPage();
2932
 
2933
                    if (!page)
2934
                    {
2935
                        MSG_DEBUG("Internal error: No actual page found!");
2936
                        return false;
2937
                    }
2938
 
2939
                    TSettings *settings = gPageManager->getSettings();
2940
 
2941
                    if (settings->getPowerUpPage().compare(page->getName()) != 0)
2942
                        gPageManager->setPage(settings->getPowerUpPage());
2943
                }
2944
            }
2945
            else if (iter->pfType == "Prev")        // Flip to previous page
2946
            {
2947
                if (gPageManager)
2948
                {
2949
                    int old = gPageManager->getPreviousPageNumber();
2950
 
2951
                    if (old > 0)
2952
                        gPageManager->setPage(old);
2953
                }
2954
            }
2955
            else if (iter->pfType == "sToggle")     // Toggle popup state
2956
            {
2957
                if (gPageManager)
2958
                {
14 andreas 2959
                    TSubPage *page = gPageManager->getSubPage(iter->pfName);
11 andreas 2960
 
14 andreas 2961
                    if (page && page->isVisible())
2962
                        gPageManager->hideSubPage(iter->pfName);
2963
                    else if (page)
2964
                        gPageManager->showSubPage(iter->pfName);
2965
                }
2966
            }
11 andreas 2967
        }
2968
    }
15 andreas 2969
 
2970
    return true;
11 andreas 2971
}
2972
 
7 andreas 2973
/**
2974
 * Based on the pixels in the \a basePix, the function decides whether to return
2975
 * the value of \a col1 or \a col2. A red pixel returns the color \a col1 and
2976
 * a green pixel returns the color \a col2. If there is no red and no green
2977
 * pixel, a transparent pixel is returned.
2978
 *
2979
 * @param basePix
2980
 * This is a pixel from a mask containing red and/or green pixels.
2981
 *
2982
 * @param maskPix
2983
 * This is a pixel from a mask containing more or less tranparent pixels. If
2984
 * the alpha channel of \a basePix is 0 (transparent) this pixel is returned.
2985
 *
2986
 * @param col1
2987
 * The first color.
2988
 *
2989
 * @param col2
2990
 * The second color.
2991
 *
2992
 * @return
2993
 * An array containing the color for one pixel.
2994
 */
10 andreas 2995
SkColor TButton::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
7 andreas 2996
{
2997
    uint alpha = SkColorGetA(basePix);
2998
    uint red = SkColorGetR(basePix);
2999
    uint green = SkColorGetG(basePix);
3000
 
3001
    if (alpha == 0)
3002
        return maskPix;
3003
 
3004
    if (red && green)
3005
    {
3006
        uint newR = (SkColorGetR(col1) + SkColorGetR(col2) / 2) & 0x0ff;
3007
        uint newG = (SkColorGetG(col1) + SkColorGetG(col2) / 2) & 0x0ff;
3008
        uint newB = (SkColorGetB(col1) + SkColorGetB(col2) / 2) & 0x0ff;
3009
        uint newA = (SkColorGetA(col1) + SkColorGetA(col2) / 2) & 0x0ff;
3010
 
3011
        return SkColorSetARGB(newA, newR, newG, newB);
3012
    }
3013
 
3014
    if (red)
3015
        return col1;
3016
 
3017
    if (green)
3018
        return col2;
3019
 
3020
    return SK_ColorTRANSPARENT; // transparent pixel
3021
}
3022
 
3023
TEXT_EFFECT TButton::textEffect(const std::string& effect)
3024
{
3025
    DECL_TRACER("TButton::textEffect(const std::string& effect)");
3026
 
3027
    if (effect == "Outline-S")
3028
        return EFFECT_OUTLINE_S;
3029
    else if (effect == "Outline-M")
3030
        return EFFECT_OUTLINE_M;
3031
    else if (effect == "Outline-L")
3032
        return EFFECT_OUTLINE_L;
3033
    else if (effect == "Outline-X")
3034
        return EFFECT_OUTLINE_X;
3035
    else if (effect == "Glow-S")
3036
        return EFFECT_GLOW_S;
3037
    else if (effect == "Glow-M")
3038
        return EFFECT_GLOW_M;
3039
    else if (effect == "Glow-L")
3040
        return EFFECT_GLOW_L;
3041
    else if (effect == "Glow-X")
3042
        return EFFECT_GLOW_X;
3043
    else if (effect == "Soft Drop Shadow 1")
3044
        return EFFECT_SOFT_DROP_SHADOW_1;
3045
    else if (effect == "Soft Drop Shadow 2")
3046
        return EFFECT_SOFT_DROP_SHADOW_2;
3047
    else if (effect == "Soft Drop Shadow 3")
3048
        return EFFECT_SOFT_DROP_SHADOW_3;
3049
    else if (effect == "Soft Drop Shadow 4")
3050
        return EFFECT_SOFT_DROP_SHADOW_4;
3051
    else if (effect == "Soft Drop Shadow 5")
3052
        return EFFECT_SOFT_DROP_SHADOW_5;
3053
    else if (effect == "Soft Drop Shadow 6")
3054
        return EFFECT_SOFT_DROP_SHADOW_6;
3055
    else if (effect == "Soft Drop Shadow 7")
3056
        return EFFECT_SOFT_DROP_SHADOW_7;
3057
    else if (effect == "Soft Drop Shadow 8")
3058
        return EFFECT_SOFT_DROP_SHADOW_8;
3059
    else if (effect == "Medium Drop Shadow 1")
3060
        return EFFECT_MEDIUM_DROP_SHADOW_1;
3061
    else if (effect == "Medium Drop Shadow 2")
3062
        return EFFECT_MEDIUM_DROP_SHADOW_2;
3063
    else if (effect == "Medium Drop Shadow 3")
3064
        return EFFECT_MEDIUM_DROP_SHADOW_3;
3065
    else if (effect == "Medium Drop Shadow 4")
3066
        return EFFECT_MEDIUM_DROP_SHADOW_4;
3067
    else if (effect == "Medium Drop Shadow 5")
3068
        return EFFECT_MEDIUM_DROP_SHADOW_5;
3069
    else if (effect == "Medium Drop Shadow 6")
3070
        return EFFECT_MEDIUM_DROP_SHADOW_6;
3071
    else if (effect == "Medium Drop Shadow 7")
3072
        return EFFECT_MEDIUM_DROP_SHADOW_7;
3073
    else if (effect == "Medium Drop Shadow 8")
3074
        return EFFECT_MEDIUM_DROP_SHADOW_8;
3075
    else if (effect == "Hard Drop Shadow 1")
3076
        return EFFECT_HARD_DROP_SHADOW_1;
3077
    else if (effect == "Hard Drop Shadow 2")
3078
        return EFFECT_HARD_DROP_SHADOW_2;
3079
    else if (effect == "Hard Drop Shadow 3")
3080
        return EFFECT_HARD_DROP_SHADOW_3;
3081
    else if (effect == "Hard Drop Shadow 4")
3082
        return EFFECT_HARD_DROP_SHADOW_4;
3083
    else if (effect == "Hard Drop Shadow 5")
3084
        return EFFECT_HARD_DROP_SHADOW_5;
3085
    else if (effect == "Hard Drop Shadow 6")
3086
        return EFFECT_HARD_DROP_SHADOW_6;
3087
    else if (effect == "Hard Drop Shadow 7")
3088
        return EFFECT_HARD_DROP_SHADOW_7;
3089
    else if (effect == "Hard Drop Shadow 8")
3090
        return EFFECT_HARD_DROP_SHADOW_8;
3091
    else if (effect == "Soft Drop Shadow 1 with outline")
3092
        return EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE;
3093
    else if (effect == "Soft Drop Shadow 2 with outline")
3094
        return EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE;
3095
    else if (effect == "Soft Drop Shadow 3 with outline")
3096
        return EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE;
3097
    else if (effect == "Soft Drop Shadow 4 with outline")
3098
        return EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE;
3099
    else if (effect == "Soft Drop Shadow 5 with outline")
3100
        return EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE;
3101
    else if (effect == "Soft Drop Shadow 6 with outline")
3102
        return EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE;
3103
    else if (effect == "Soft Drop Shadow 7 with outline")
3104
        return EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE;
3105
    else if (effect == "Soft Drop Shadow 8 with outline")
3106
        return EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE;
3107
    else if (effect == "Medium Drop Shadow 1 with outline")
3108
        return EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE;
3109
    else if (effect == "Medium Drop Shadow 2 with outline")
3110
        return EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE;
3111
    else if (effect == "Medium Drop Shadow 3 with outline")
3112
        return EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE;
3113
    else if (effect == "Medium Drop Shadow 4 with outline")
3114
        return EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE;
3115
    else if (effect == "Medium Drop Shadow 5 with outline")
3116
        return EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE;
3117
    else if (effect == "Medium Drop Shadow 6 with outline")
3118
        return EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE;
3119
    else if (effect == "Medium Drop Shadow 7 with outline")
3120
        return EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE;
3121
    else if (effect == "Medium Drop Shadow 8 with outline")
3122
        return EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE;
3123
    else if (effect == "Hard Drop Shadow 1 with outline")
3124
        return EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE;
3125
    else if (effect == "Hard Drop Shadow 2 with outline")
3126
        return EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE;
3127
    else if (effect == "Hard Drop Shadow 3 with outline")
3128
        return EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE;
3129
    else if (effect == "Hard Drop Shadow 4 with outline")
3130
        return EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE;
3131
    else if (effect == "Hard Drop Shadow 5 with outline")
3132
        return EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE;
3133
    else if (effect == "Hard Drop Shadow 6 with outline")
3134
        return EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE;
3135
    else if (effect == "Hard Drop Shadow 7 with outline")
3136
        return EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE;
3137
    else if (effect == "Hard Drop Shadow 8 with outline")
3138
        return EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE;
3139
 
3140
    return EFFECT_NONE;
3141
}
15 andreas 3142
 
3143
bool TButton::isSystemButton()
3144
{
3145
    DECL_TRACER("TButton::isSystemButton()");
3146
 
3147
    int i = 0;
3148
 
3149
    while (sysButtons[i].channel)
3150
    {
3151
        if (ap == 0 && ad == sysButtons[i].channel)
3152
            return true;
3153
 
3154
        i++;
3155
    }
3156
 
3157
    return false;
3158
}
21 andreas 3159
 
3160
THR_REFRESH_t *TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)
3161
{
3162
    DECL_TRACER("TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)");
3163
 
3164
    THR_REFRESH_t *p = mThrRefresh;
3165
    THR_REFRESH_t *r, *last = p;
3166
 
3167
    if (!refr || !handle || !parent || bi <= 0)
3168
    {
3169
        MSG_ERROR("Invalid parameter!");
3170
        return nullptr;
3171
    }
3172
 
3173
    r = new THR_REFRESH_t;
3174
    r->mImageRefresh = refr;
3175
    r->handle = handle;
3176
    r->parent = parent;
3177
    r->bi = bi;
3178
    r->next = nullptr;
3179
 
3180
    // If the chain is empty, add the new item;
3181
    if (!mThrRefresh)
3182
        mThrRefresh = r;
3183
    else    // Find the end and append the item
3184
    {
3185
        while (p)
3186
        {
3187
            last = p;
3188
 
3189
            if (p->handle == handle && p->parent == parent && p->bi == bi)
3190
            {
3191
                MSG_WARNING("Duplicate button found! Didn't add it again.");
3192
                delete r;
3193
                return p;
3194
            }
3195
 
3196
            p = p->next;
3197
        }
3198
 
3199
        last->next = r;
3200
    }
3201
 
3202
    MSG_DEBUG("New dynamic button added.");
3203
    return r;
3204
}
3205
 
3206
THR_REFRESH_t *TButton::_findResource(ulong handle, ulong parent, int bi)
3207
{
3208
    DECL_TRACER("TButton::_findResource(ulong handle, ulong parent, int bi)");
3209
 
3210
    THR_REFRESH_t *p = mThrRefresh;
3211
 
3212
    while (p)
3213
    {
3214
        if (p->handle == handle && p->parent == parent && p->bi == bi)
3215
            return p;
3216
 
3217
        p = p->next;
3218
    }
3219
 
3220
    return nullptr;
3221
}