Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
99 andreas 2
 * Copyright (C) 2020 to 2022 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>
40 andreas 21
#include <algorithm>
67 andreas 22
#include <codecvt>
6 andreas 23
 
24
#include <include/core/SkPixmap.h>
3 andreas 25
#include <include/core/SkSize.h>
4 andreas 26
#include <include/core/SkColor.h>
7 andreas 27
#include <include/core/SkFont.h>
28
#include <include/core/SkTypeface.h>
8 andreas 29
#include <include/core/SkFontMetrics.h>
19 andreas 30
#include <include/core/SkTextBlob.h>
8 andreas 31
#include <include/core/SkRegion.h>
32
#include <include/core/SkImageFilter.h>
33
#include <include/effects/SkImageFilters.h>
34
#include <include/core/SkPath.h>
10 andreas 35
#include <include/core/SkSurfaceProps.h>
26 andreas 36
#include <include/core/SkFilterQuality.h>
66 andreas 37
#include <include/core/SkMaskFilter.h>
6 andreas 38
 
23 andreas 39
#ifdef __ANDROID__
40
#include <QtAndroidExtras/QAndroidJniObject>
41
#include <QtAndroid>
42
#endif
43
 
2 andreas 44
#include "tbutton.h"
94 andreas 45
#include "thttpclient.h"
3 andreas 46
#include "terror.h"
47
#include "tconfig.h"
4 andreas 48
#include "tresources.h"
8 andreas 49
#include "ticons.h"
14 andreas 50
#include "tamxnet.h"
51
#include "tobject.h"
52
#include "tpagemanager.h"
71 andreas 53
#include "tsystemsound.h"
97 andreas 54
#include "timgcache.h"
2 andreas 55
 
15 andreas 56
using std::exception;
3 andreas 57
using std::string;
58
using std::vector;
59
using std::unique_ptr;
60
using std::map;
61
using std::pair;
15 andreas 62
using std::thread;
63
using std::atomic;
64
using std::mutex;
21 andreas 65
using std::bind;
2 andreas 66
using namespace Button;
76 andreas 67
using namespace Expat;
2 andreas 68
 
21 andreas 69
#define MAX_BUFFER      65536
70
 
8 andreas 71
extern TIcons *gIcons;
14 andreas 72
extern amx::TAmxNet *gAmxNet;
26 andreas 73
extern TPageManager *gPageManager;
8 andreas 74
 
21 andreas 75
THR_REFRESH_t *TButton::mThrRefresh = nullptr;
94 andreas 76
vector<BITMAP_CACHE> nBitmapCache;     // Holds the images who are delayed because they are external
21 andreas 77
 
15 andreas 78
mutex mutex_button;
79
mutex mutex_bargraph;
80
mutex mutex_sysdraw;
94 andreas 81
mutex mutex_bmCache;
15 andreas 82
 
81 andreas 83
/**
84
 * The following table defines some of the system borders. It is mostly a
85
 * fallback table but defines whether a border should be calculated internaly
86
 * or constructed out of the images in the system border folder. The later is
87
 * only possible if this folder exist and contains the system images from AMX.
88
 * This images could be retrieved by sending a full surface with the system
89
 * files included from TPDesign4.
90
 * All borders not listed in this table are constructed out of the system
91
 * border images, if they exist.
92
 */
4 andreas 93
SYSBORDER_t sysBorders[] = {
81 andreas 94
//   ID   Name                  AMX number  Style          width radius calc
95
    {  1, (char *)"Single Line",         0, (char *)"solid",   1,   0, true },
96
    {  2, (char *)"Double Line",         0, (char *)"solid",   2,   0, true },
97
    {  3, (char *)"Quad Line",           0, (char *)"solid",   4,   0, true },
98
    {  4, (char *)"Picture Frame",       0, (char *)"double",  0,   0, false },
99
    {  5, (char *)"Circle 15",           8, (char *)"solid",   2,   7, true },
100
    {  6, (char *)"Circle 25",           9, (char *)"solid",   2,  14, true },
101
    {  7, (char *)"Circle 35",          10, (char *)"solid",   2,  21, true },
102
    {  8, (char *)"Circle 45",          11, (char *)"solid",   2,  28, true },
103
    {  9, (char *)"Circle 55",          12, (char *)"solid",   2,  35, true },
104
    { 10, (char *)"Circle 65",          13, (char *)"solid",   2,  42, true },
105
    { 11, (char *)"Circle 75",          14, (char *)"solid",   2,  49, true },
106
    { 12, (char *)"Circle 85",          15, (char *)"solid",   2,  56, true },
107
    { 13, (char *)"Circle 95",          16, (char *)"solid",   2,  63, true },
108
    { 14, (char *)"Circle 105",         17, (char *)"solid",   2,  70, true },
109
    { 15, (char *)"Circle 115",         18, (char *)"solid",   2,  77, true },
110
    { 16, (char *)"Circle 125",         19, (char *)"solid",   2,  84, true },
111
    { 17, (char *)"Circle 135",         20, (char *)"solid",   2,  91, true },
112
    { 18, (char *)"Circle 145",         21, (char *)"solid",   2,  98, true },
113
    { 19, (char *)"Circle 155",         22, (char *)"solid",   2, 105, true },
114
    { 20, (char *)"Circle 165",         23, (char *)"solid",   2, 112, true },
115
    { 21, (char *)"Circle 175",         24, (char *)"solid",   2, 119, true },
116
    { 22, (char *)"Circle 185",         25, (char *)"solid",   2, 126, true },
117
    { 23, (char *)"Circle 195",         26, (char *)"solid",   2, 133, true },
118
    { 24, (char *)"AMX Elite Inset -L",  0, (char *)"groove", 20,   0, false },
119
    { 25, (char *)"AMX Elite Raised -L", 0, (char *)"ridge",  20,   0, false },
120
    { 26, (char *)"AMX Elite Inset -M",  0, (char *)"groove", 10,   0, false },
121
    { 27, (char *)"AMX Elite Raised -M", 0, (char *)"ridge",  10,   0, false },
122
    { 28, (char *)"AMX Elite Inset -S",  0, (char *)"groove",  4,   0, false },
123
    { 29, (char *)"AMX Elite Raised -S", 0, (char *)"ridge",   4,   0, false },
124
    { 30, (char *)"Bevel Inset -L",      0, (char *)"inset",  20,   0, false },
125
    { 31, (char *)"Bevel Raised -L",     0, (char *)"outset", 20,   0, false },
126
    { 32, (char *)"Bevel Inset -M",      0, (char *)"inset",  10,   0, false },
127
    { 33, (char *)"Bevel Raised -M",     0, (char *)"outset", 10,   0, false },
128
    { 34, (char *)"Bevel Inset -S",      0, (char *)"inset",   4,   0, false },
129
    { 35, (char *)"Bevel Raised -S",     0, (char *)"outset",  4,   0, false },
8 andreas 130
    {  0, nullptr, 0, nullptr, 0, 0 }
4 andreas 131
};
132
 
15 andreas 133
SYSBUTTONS_t sysButtons[] = {
51 andreas 134
    {    8, MULTISTATE_BARGRAPH,  12, 0,  11 },  // Connection status
71 andreas 135
    {   17, GENERAL,               2, 0,   0 },  // Button sounds on/off
113 andreas 136
    {   73, GENERAL,               2, 0,   0 },  // Enter setup page
137
    {   80, GENERAL,               2, 0,   0 },  // Shutdown program
51 andreas 138
    {   81, MULTISTATE_BARGRAPH,   6, 1,   6 },  // Network signal stength
139
    {  122, TEXT_INPUT,            2, 0,   0 },  // IP Address of server or domain name
140
    {  123, TEXT_INPUT,            2, 9,   0 },  // Channel number of panel
141
    {  124, TEXT_INPUT,            2, 0,   0 },  // The network port number (1319)
142
    {  141, GENERAL,               2, 0,   0 },  // Standard time
143
    {  142, GENERAL,               2, 0,   0 },  // Time AM/PM
144
    {  143, GENERAL,               2, 0,   0 },  // 24 hour time
145
    {  151, GENERAL,               2, 0,   0 },  // Date weekday
146
    {  152, GENERAL,               2, 0,   0 },  // Date mm/dd
147
    {  153, GENERAL,               2, 0,   0 },  // Date dd/mm
148
    {  154, GENERAL,               2, 0,   0 },  // Date mm/dd/yyyy
149
    {  155, GENERAL,               2, 0,   0 },  // Date dd/mm/yyyy
150
    {  156, GENERAL,               2, 0,   0 },  // Date month dd, yyyy
151
    {  157, GENERAL,               2, 0,   0 },  // Date dd month, yyyy
152
    {  158, GENERAL,               2, 0,   0 },  // Date yyyy-mm-dd
153
    {  199, TEXT_INPUT,            2, 0,   0 },  // Technical name of panel
154
    {  234, GENERAL,               2, 0,   0 },  // Battery charging/not charging
155
    {  242, BARGRAPH,              2, 0, 100 },  // Battery level
156
    { 1101, TEXT_INPUT,            2, 0,   0 },  // Path and name of the logfile
157
    {    0, NONE,                  0, 0,   0 }   // Terminate
15 andreas 158
};
4 andreas 159
 
110 andreas 160
SYSTEF_t sysTefs[] = {
161
    {  1, "Outline-S" },
162
    {  2, "Outline-M" },
163
    {  3, "Outline-L" },
164
    {  4, "Outline-X" },
165
    {  5, "Glow-S" },
166
    {  6, "Glow-M" },
167
    {  7, "Glow-L" },
168
    {  8, "Glow-X" },
169
    {  9, "Soft Drop Shadow 1" },
170
    { 10, "Soft Drop Shadow 2" },
171
    { 11, "Soft Drop Shadow 3" },
172
    { 12, "Soft Drop Shadow 4" },
173
    { 13, "Soft Drop Shadow 5" },
174
    { 14, "Soft Drop Shadow 6" },
175
    { 15, "Soft Drop Shadow 7" },
176
    { 16, "Soft Drop Shadow 8" },
177
    { 17, "Medium Drop Shadow 1" },
178
    { 18, "Medium Drop Shadow 2" },
179
    { 19, "Medium Drop Shadow 3" },
180
    { 20, "Medium Drop Shadow 4" },
181
    { 21, "Medium Drop Shadow 5" },
182
    { 22, "Medium Drop Shadow 6" },
183
    { 23, "Medium Drop Shadow 7" },
184
    { 24, "Medium Drop Shadow 8" },
185
    { 25, "Hard Drop Shadow 1" },
186
    { 26, "Hard Drop Shadow 2" },
187
    { 27, "Hard Drop Shadow 3" },
188
    { 28, "Hard Drop Shadow 4" },
189
    { 29, "Hard Drop Shadow 5" },
190
    { 30, "Hard Drop Shadow 6" },
191
    { 31, "Hard Drop Shadow 7" },
192
    { 32, "Hard Drop Shadow 8" },
193
    { 33, "Soft Drop Shadow 1 with outline" },
194
    { 34, "Soft Drop Shadow 2 with outline" },
195
    { 35, "Soft Drop Shadow 3 with outline" },
196
    { 36, "Soft Drop Shadow 4 with outline" },
197
    { 37, "Soft Drop Shadow 5 with outline" },
198
    { 38, "Soft Drop Shadow 6 with outline" },
199
    { 39, "Soft Drop Shadow 7 with outline" },
200
    { 40, "Soft Drop Shadow 8 with outline" },
201
    { 41, "Medium Drop Shadow 1 with outline" },
202
    { 42, "Medium Drop Shadow 2 with outline" },
203
    { 43, "Medium Drop Shadow 3 with outline" },
204
    { 44, "Medium Drop Shadow 4 with outline" },
205
    { 45, "Medium Drop Shadow 5 with outline" },
206
    { 46, "Medium Drop Shadow 6 with outline" },
207
    { 47, "Medium Drop Shadow 7 with outline" },
208
    { 48, "Medium Drop Shadow 8 with outline" },
209
    { 49, "Hard Drop Shadow 1 with outline" },
210
    { 50, "Hard Drop Shadow 2 with outline" },
211
    { 51, "Hard Drop Shadow 3 with outline" },
212
    { 52, "Hard Drop Shadow 4 with outline" },
213
    { 53, "Hard Drop Shadow 5 with outline" },
214
    { 54, "Hard Drop Shadow 6 with outline" },
215
    { 55, "Hard Drop Shadow 7 with outline" },
216
    { 56, "Hard Drop Shadow 8 with outline" },
217
    { 0,  "\0" }
218
};
219
 
3 andreas 220
TButton::TButton()
2 andreas 221
{
3 andreas 222
    DECL_TRACER("TButton::TButton()");
16 andreas 223
 
21 andreas 224
    mAniRunning = false;
16 andreas 225
    mLastBlink.clear();
2 andreas 226
}
227
 
3 andreas 228
TButton::~TButton()
2 andreas 229
{
3 andreas 230
    DECL_TRACER("TButton::~TButton()");
34 andreas 231
/*
3 andreas 232
    map<int, IMAGE_t>::iterator iter;
233
 
234
    for (iter = mImages.begin(); iter != mImages.end(); iter++)
235
    {
6 andreas 236
        if (!iter->second.imageMi.empty())
3 andreas 237
            iter->second.imageMi.reset();
238
 
6 andreas 239
        if (!iter->second.imageBm.empty())
3 andreas 240
            iter->second.imageBm.reset();
241
    }
34 andreas 242
*/
3 andreas 243
    mImages.clear();
15 andreas 244
 
245
    if (ap == 0 && ad == 8)
246
    {
247
        if (gAmxNet)
248
            gAmxNet->deregNetworkState(mHandle);
249
    }
250
 
251
    if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158)))
252
    {
253
        if (gAmxNet)
254
            gAmxNet->deregTimer(mHandle);
255
    }
256
 
36 andreas 257
    if (ap == 0 && ad == 81)    // Network state multi bargraph
258
    {
259
        if (gPageManager)
260
            gPageManager->unregCallbackNetState(mHandle);
261
    }
262
 
15 andreas 263
    if (mTimer)
264
    {
265
        mTimer->stop();
266
 
267
        while (mTimer->isRunning())
268
            usleep(50);
269
 
270
        delete mTimer;
271
    }
21 andreas 272
 
93 andreas 273
    if (mAniRunning)
274
    {
275
        ulong tm = nu * ru + nd * rd;
276
        mAniStop = true;
277
 
278
        while (mAniRunning)
279
            std::this_thread::sleep_for(std::chrono::milliseconds(tm * 100));
280
    }
281
 
21 andreas 282
    THR_REFRESH_t *next, *p = mThrRefresh;
283
 
284
    while (p)
285
    {
286
        if (p->mImageRefresh)
287
        {
288
            p->mImageRefresh->stop();
34 andreas 289
            int counter = 0;
21 andreas 290
 
34 andreas 291
            while (counter < 1000 && p->mImageRefresh->isRunning())
292
            {
21 andreas 293
                usleep(50);
34 andreas 294
                counter++;
295
            }
21 andreas 296
 
297
            delete p->mImageRefresh;
298
            p->mImageRefresh = nullptr;
299
        }
300
 
301
        next = p->next;
302
        delete p;
303
        p = next;
304
    }
35 andreas 305
 
306
    mThrRefresh = nullptr;
2 andreas 307
}
3 andreas 308
 
76 andreas 309
size_t TButton::initialize(TExpat *xml, size_t index)
3 andreas 310
{
78 andreas 311
    DECL_TRACER("TButton::initialize(TExpat *xml, size_t index)");
3 andreas 312
 
76 andreas 313
    if (!xml || index == TExpat::npos)
3 andreas 314
    {
315
        MSG_ERROR("Invalid NULL parameter passed!");
316
        TError::setError();
76 andreas 317
        return TExpat::npos;
3 andreas 318
    }
319
 
76 andreas 320
    vector<ATTRIBUTE_t> attrs = xml->getAttributes(index);
321
    string stype = xml->getAttribute("type", attrs);
3 andreas 322
    type = getButtonType(stype);
323
    MSG_DEBUG("Button type: " << stype << " --> " << type);
76 andreas 324
    string ename, content;
325
    size_t oldIndex = index;
3 andreas 326
 
76 andreas 327
    while((index = xml->getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
3 andreas 328
    {
329
        if (ename.compare("bi") == 0)
76 andreas 330
            bi = xml->convertElementToInt(content);
3 andreas 331
        else if (ename.compare("na") == 0)
76 andreas 332
            na = content;
3 andreas 333
        else if (ename.compare("bd") == 0)
76 andreas 334
            bd = content;
3 andreas 335
        else if (ename.compare("lt") == 0)
76 andreas 336
            lt = xml->convertElementToInt(content);
3 andreas 337
        else if (ename.compare("tp") == 0)
76 andreas 338
            tp = xml->convertElementToInt(content);
3 andreas 339
        else if (ename.compare("wt") == 0)
76 andreas 340
            wt = xml->convertElementToInt(content);
3 andreas 341
        else if (ename.compare("ht") == 0)
76 andreas 342
            ht = xml->convertElementToInt(content);
3 andreas 343
        else if (ename.compare("zo") == 0)
76 andreas 344
            zo = xml->convertElementToInt(content);
3 andreas 345
        else if (ename.compare("hs") == 0)
76 andreas 346
            hs = content;
3 andreas 347
        else if (ename.compare("bs") == 0)
76 andreas 348
            bs = content;
3 andreas 349
        else if (ename.compare("fb") == 0)
76 andreas 350
            fb = getButtonFeedback(content);
3 andreas 351
        else if (ename.compare("ap") == 0)
76 andreas 352
            ap = xml->convertElementToInt(content);
3 andreas 353
        else if (ename.compare("ad") == 0)
76 andreas 354
            ad = xml->convertElementToInt(content);
3 andreas 355
        else if (ename.compare("ch") == 0)
76 andreas 356
            ch = xml->convertElementToInt(content);
3 andreas 357
        else if (ename.compare("cp") == 0)
76 andreas 358
            cp = xml->convertElementToInt(content);
3 andreas 359
        else if (ename.compare("lp") == 0)
76 andreas 360
            lp = xml->convertElementToInt(content);
3 andreas 361
        else if (ename.compare("lv") == 0)
76 andreas 362
            lv = xml->convertElementToInt(content);
3 andreas 363
        else if (ename.compare("dr") == 0)
76 andreas 364
            dr = content;
3 andreas 365
        else if (ename.compare("va") == 0)
76 andreas 366
            va = xml->convertElementToInt(content);
3 andreas 367
        else if (ename.compare("rm") == 0)
76 andreas 368
            rm = xml->convertElementToInt(content);
3 andreas 369
        else if (ename.compare("nu") == 0)
76 andreas 370
            nu = xml->convertElementToInt(content);
3 andreas 371
        else if (ename.compare("nd") == 0)
76 andreas 372
            nd = xml->convertElementToInt(content);
3 andreas 373
        else if (ename.compare("ar") == 0)
76 andreas 374
            ar = xml->convertElementToInt(content);
3 andreas 375
        else if (ename.compare("ru") == 0)
76 andreas 376
            ru = xml->convertElementToInt(content);
3 andreas 377
        else if (ename.compare("rd") == 0)
76 andreas 378
            rd = xml->convertElementToInt(content);
3 andreas 379
        else if (ename.compare("lu") == 0)
76 andreas 380
            lu = xml->convertElementToInt(content);
3 andreas 381
        else if (ename.compare("ld") == 0)
76 andreas 382
            ld = xml->convertElementToInt(content);
3 andreas 383
        else if (ename.compare("rv") == 0)
76 andreas 384
            rv = xml->convertElementToInt(content);
3 andreas 385
        else if (ename.compare("rl") == 0)
76 andreas 386
            rl = xml->convertElementToInt(content);
3 andreas 387
        else if (ename.compare("rh") == 0)
76 andreas 388
            rh = xml->convertElementToInt(content);
3 andreas 389
        else if (ename.compare("ri") == 0)
76 andreas 390
            ri = xml->convertElementToInt(content);
3 andreas 391
        else if (ename.compare("rn") == 0)
76 andreas 392
            rn = xml->convertElementToInt(content);
49 andreas 393
        else if (ename.compare("lf") == 0)
76 andreas 394
            lf = content;
3 andreas 395
        else if (ename.compare("sd") == 0)
76 andreas 396
            sd = content;
3 andreas 397
        else if (ename.compare("sc") == 0)
76 andreas 398
            sc = content;
3 andreas 399
        else if (ename.compare("mt") == 0)
76 andreas 400
            mt = xml->convertElementToInt(content);
3 andreas 401
        else if (ename.compare("dt") == 0)
76 andreas 402
            dt = content;
3 andreas 403
        else if (ename.compare("im") == 0)
76 andreas 404
            im = content;
3 andreas 405
        else if (ename.compare("op") == 0)
76 andreas 406
            op = content;
51 andreas 407
        else if (ename.compare("pc") == 0)
76 andreas 408
            pc = content;
51 andreas 409
        else if (ename.compare("hd") == 0)
76 andreas 410
            hd = xml->convertElementToInt(content);
51 andreas 411
        else if (ename.compare("da") == 0)
76 andreas 412
            da = xml->convertElementToInt(content);
51 andreas 413
        else if (ename.compare("ac") == 0)
414
        {
76 andreas 415
            ac_di = xml->getAttributeInt("di", attrs);
51 andreas 416
        }
3 andreas 417
        else if (ename.compare("pf") == 0)
418
        {
419
            PUSH_FUNC_T pf;
76 andreas 420
            pf.pfName = content;
421
            pf.pfType = xml->getAttribute("type", attrs);
3 andreas 422
            pushFunc.push_back(pf);
423
        }
424
        else if (ename.compare("sr") == 0)
425
        {
426
            SR_T bsr;
76 andreas 427
            bsr.number = xml->getAttributeInt("number", attrs);
428
            string e;
3 andreas 429
 
76 andreas 430
            while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
3 andreas 431
            {
432
                if (e.compare("do") == 0)
76 andreas 433
                    bsr._do = content;
3 andreas 434
                else if (e.compare("bs") == 0)
76 andreas 435
                    bsr.bs = content;
3 andreas 436
                else if (e.compare("mi") == 0)
76 andreas 437
                    bsr.mi = content;
3 andreas 438
                else if (e.compare("cb") == 0)
76 andreas 439
                    bsr.cb = content;
3 andreas 440
                else if (e.compare("cf") == 0)
76 andreas 441
                    bsr.cf = content;
3 andreas 442
                else if (e.compare("ct") == 0)
76 andreas 443
                    bsr.ct = content;
3 andreas 444
                else if (e.compare("ec") == 0)
76 andreas 445
                    bsr.ec = content;
3 andreas 446
                else if (e.compare("bm") == 0)
447
                {
76 andreas 448
                    bsr.bm = content;
449
                    bsr.dynamic = ((xml->getAttributeInt("dynamic", attrs) == 1) ? true : false);
3 andreas 450
                }
451
                else if (e.compare("sd") == 0)
76 andreas 452
                    bsr.sd = content;
3 andreas 453
                else if (e.compare("sb") == 0)
76 andreas 454
                    bsr.sb = xml->convertElementToInt(content);
3 andreas 455
                else if (e.compare("ii") == 0)
76 andreas 456
                    bsr.ii = xml->convertElementToInt(content);
3 andreas 457
                else if (e.compare("ji") == 0)
76 andreas 458
                    bsr.ji = xml->convertElementToInt(content);
3 andreas 459
                else if (e.compare("jb") == 0)
76 andreas 460
                    bsr.jb = xml->convertElementToInt(content);
10 andreas 461
                else if (e.compare("bx") == 0)
76 andreas 462
                    bsr.bx = xml->convertElementToInt(content);
10 andreas 463
                else if (e.compare("by") == 0)
76 andreas 464
                    bsr.by = xml->convertElementToInt(content);
3 andreas 465
                else if (e.compare("ix") == 0)
76 andreas 466
                    bsr.ix = xml->convertElementToInt(content);
3 andreas 467
                else if (e.compare("iy") == 0)
76 andreas 468
                    bsr.iy = xml->convertElementToInt(content);
3 andreas 469
                else if (e.compare("fi") == 0)
76 andreas 470
                    bsr.fi = xml->convertElementToInt(content);
3 andreas 471
                else if (e.compare("te") == 0)
76 andreas 472
                    bsr.te = content;
3 andreas 473
                else if (e.compare("jt") == 0)
76 andreas 474
                    bsr.jt = (TEXT_ORIENTATION)xml->convertElementToInt(content);
3 andreas 475
                else if (e.compare("tx") == 0)
76 andreas 476
                    bsr.tx = xml->convertElementToInt(content);
3 andreas 477
                else if (e.compare("ty") == 0)
76 andreas 478
                    bsr.ty = xml->convertElementToInt(content);
3 andreas 479
                else if (e.compare("ww") == 0)
76 andreas 480
                    bsr.ww = xml->convertElementToInt(content);
3 andreas 481
                else if (e.compare("et") == 0)
76 andreas 482
                    bsr.et = xml->convertElementToInt(content);
3 andreas 483
                else if (e.compare("oo") == 0)
76 andreas 484
                    bsr.oo = xml->convertElementToInt(content);
3 andreas 485
 
76 andreas 486
                oldIndex = index;
3 andreas 487
            }
488
 
489
            sr.push_back(bsr);
490
        }
491
 
76 andreas 492
        if (index == TExpat::npos)
493
            index = oldIndex + 1;
494
    }
3 andreas 495
 
100 andreas 496
    visible = !hd;  // set the initial visibility
497
/*
83 andreas 498
    if (sr.size() > 0 && TStreamError::checkFilter(HLOG_DEBUG))
76 andreas 499
    {
500
        MSG_DEBUG("bi  : " << bi);
501
        MSG_DEBUG("na  : " << na);
502
        MSG_DEBUG("type: " << type);
503
        MSG_DEBUG("lt  : " << lt);
504
        MSG_DEBUG("tp  : " << tp);
505
        MSG_DEBUG("wt  : " << wt);
506
        MSG_DEBUG("ht  : " << ht);
507
 
508
        vector<SR_T>::iterator iter;
509
        size_t pos = 1;
510
 
511
        for (iter = sr.begin(); iter != sr.end(); ++iter)
3 andreas 512
        {
76 andreas 513
            MSG_DEBUG("   " << pos << ": id: " << iter->number);
514
            MSG_DEBUG("   " << pos << ": bs: " << iter->bs);
515
            MSG_DEBUG("   " << pos << ": cb: " << iter->cb);
516
            MSG_DEBUG("   " << pos << ": cf: " << iter->cf);
517
            MSG_DEBUG("   " << pos << ": ct: " << iter->ct);
518
            MSG_DEBUG("   " << pos << ": ec: " << iter->ec);
519
            MSG_DEBUG("   " << pos << ": bm: " << iter->bm);
520
            MSG_DEBUG("   " << pos << ": mi: " << iter->mi);
521
            MSG_DEBUG("   " << pos << ": fi: " << iter->fi);
522
            MSG_DEBUG("   " << pos << ": te: " << iter->te);
523
            pos++;
3 andreas 524
        }
525
    }
100 andreas 526
*/
76 andreas 527
    if (index == TExpat::npos)
528
    {
529
        MSG_DEBUG("Returning old index " << (oldIndex + 1));
530
        return oldIndex + 1;
531
    }
532
 
533
    MSG_DEBUG("Returning index " << index);
534
    return index;
3 andreas 535
}
536
 
43 andreas 537
bool TButton::createSoftButton(const EXTBUTTON_t& bt)
538
{
539
    DECL_TRACER("TButton::createSoftButton(const EXTBUTTON_t& bt)");
540
 
541
    if (bt.sr.size() < 2)
542
    {
543
        MSG_ERROR("Button " << bt.bi << ": " << bt.na << " has less than 2 states!");
544
        return false;
545
    }
546
 
547
    MSG_DEBUG("Adding soft button " << bt.bi << ": " << bt.na);
548
    type = bt.type;
549
    bi = bt.bi;
550
    na = bt.na;
551
    lt = bt.lt;
552
    tp = bt.tp;
553
    wt = bt.wt;
554
    ht = bt.ht;
555
    zo = bt.zo;
556
    hs = bt.hs;
557
    bs = bt.bs;
558
    fb = bt.fb;
559
    ap = bt.ap;
560
    ad = bt.ad;
561
    lp = bt.lp;
562
    lv = bt.lv;
563
    dr = bt.dr;
564
    lu = bt.lu;
565
    ld = bt.ld;
566
    rl = bt.rl;
567
    rh = bt.rh;
568
    rn = bt.rn;
569
    sc = bt.sc;
570
    sr = bt.sr;
571
    return true;
572
}
573
 
50 andreas 574
BITMAP_t TButton::getLastImage()
575
{
576
    DECL_TRACER("TButton::getLastImage()");
577
 
578
    BITMAP_t image;
579
    image.buffer = (unsigned char *)mLastImage.getPixels();
580
    image.rowBytes = mLastImage.info().minRowBytes();
581
    image.width = mLastImage.info().width();
582
    image.height = mLastImage.info().height();
583
    return image;
584
}
585
 
586
FONT_T TButton::getFont()
587
{
588
    DECL_TRACER("TButton::getFont()");
589
 
590
    if (!mFonts)
591
    {
592
        MSG_ERROR("No fonts available!");
593
        return FONT_T();
594
    }
595
 
596
    return mFonts->getFont(sr[mActInstance].fi);
597
}
598
 
51 andreas 599
FONT_STYLE TButton::getFontStyle()
50 andreas 600
{
601
    DECL_TRACER("TButton::getFontStyle()");
602
 
603
    if (!mFonts)
604
    {
605
        MSG_ERROR("No fonts available!");
51 andreas 606
        return FONT_NONE;
50 andreas 607
    }
608
 
609
    return mFonts->getStyle(sr[mActInstance].fi);
610
}
611
 
3 andreas 612
BUTTONTYPE TButton::getButtonType(const string& bt)
613
{
614
    DECL_TRACER("TButton::getButtonType(const string& bt)");
615
 
616
    if (bt.compare("general") == 0)
617
        return GENERAL;
38 andreas 618
    else if (bt.compare("multi-state general") == 0 || bt.compare("multiGeneral") == 0)
3 andreas 619
        return MULTISTATE_GENERAL;
620
    else if (bt.compare("bargraph") == 0)
621
        return BARGRAPH;
38 andreas 622
    else if (bt.compare("multi-state bargraph") == 0 || bt.compare("multiBargraph") == 0)
3 andreas 623
        return MULTISTATE_BARGRAPH;
624
    else if (bt.compare("joistick") == 0)
625
        return JOISTICK;
50 andreas 626
    else if (bt.compare("text input") == 0 || bt.compare("textArea") == 0)
3 andreas 627
        return TEXT_INPUT;
628
    else if (bt.compare("computer control") == 0)
629
        return COMPUTER_CONTROL;
630
    else if (bt.compare("take note") == 0)
631
        return TAKE_NOTE;
632
    else if (bt.compare("sub-page view") == 0)
633
        return SUBPAGE_VIEW;
634
 
635
    return NONE;
636
}
637
 
638
FEEDBACK TButton::getButtonFeedback(const string& fb)
639
{
640
    DECL_TRACER("TButton::getButtonFeedback(const string& fb)");
641
 
642
    if (fb.compare("channel") == 0)
643
        return FB_CHANNEL;
644
    else if (fb.compare("inverted channel") == 0)
645
        return FB_INV_CHANNEL;
646
    else if (fb.compare("always on") == 0)
647
        return FB_ALWAYS_ON;
648
    else if (fb.compare("momentary") == 0)
649
        return FB_MOMENTARY;
650
    else if (fb.compare("blink") == 0)
651
        return FB_BLINK;
652
 
653
    return FB_NONE;
654
}
655
 
40 andreas 656
bool TButton::createButtons(bool force)
3 andreas 657
{
6 andreas 658
    DECL_TRACER("TButton::createButtons()");
3 andreas 659
 
33 andreas 660
    if (prg_stopped)
661
        return false;
35 andreas 662
 
46 andreas 663
    if (force)
664
        MSG_TRACE("Creating of image is forced!");
665
 
3 andreas 666
    // Get the images, if there any
667
    vector<SR_T>::iterator srIter;
6 andreas 668
    int i = 0;
3 andreas 669
 
118 andreas 670
    for (srIter = sr.begin(); srIter != sr.end(); ++srIter)
3 andreas 671
    {
672
        int number = srIter->number;
673
        IMAGE_t img;
674
 
21 andreas 675
        if (srIter->sb > 0)
676
            continue;
677
 
46 andreas 678
        std::map<int, IMAGE_t>::iterator imgIter = mImages.find(number);
679
 
40 andreas 680
        if (!force)
681
        {
682
            if (imgIter != mImages.end())   // If the image already exist, do not load it again.
683
                continue;
684
        }
46 andreas 685
        else if (imgIter != mImages.end())  // We must first erase the image
686
        {
687
            if (!imgIter->second.imageBm.empty())
688
                imgIter->second.imageBm.reset();
40 andreas 689
 
46 andreas 690
            if (!imgIter->second.imageMi.empty())
691
                imgIter->second.imageMi.reset();
692
 
693
            mImages.erase(imgIter);
694
        }
695
 
3 andreas 696
        if (!srIter->mi.empty())        // Do we have a chameleon image?
697
        {
4 andreas 698
            sk_sp<SkData> image;
699
 
700
            if (!(image = readImage(srIter->mi)))
3 andreas 701
                return false;
702
 
6 andreas 703
            DecodeDataToBitmap(image, &img.imageMi);
3 andreas 704
 
6 andreas 705
            if (img.imageMi.empty())
3 andreas 706
            {
707
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
708
                return false;
709
            }
710
 
6 andreas 711
            srIter->mi_width = img.imageMi.dimensions().width();
712
            srIter->mi_height = img.imageMi.dimensions().height();
3 andreas 713
        }
714
 
715
        if (!srIter->bm.empty())        // Do we have a bitmap?
716
        {
4 andreas 717
            sk_sp<SkData> image;
718
 
719
            if (!(image = readImage(srIter->bm)))
3 andreas 720
                return false;
721
 
6 andreas 722
            DecodeDataToBitmap(image, &img.imageBm);
3 andreas 723
 
6 andreas 724
            if (img.imageBm.empty())
3 andreas 725
            {
726
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
727
                return false;
728
            }
729
 
6 andreas 730
            srIter->bm_width = img.imageBm.dimensions().width();
731
            srIter->bm_height = img.imageBm.dimensions().height();
3 andreas 732
        }
733
 
734
        mImages.insert(pair<int, IMAGE_t>(number, img));
6 andreas 735
        i++;
3 andreas 736
    }
737
 
4 andreas 738
    return true;
739
}
3 andreas 740
 
16 andreas 741
void TButton::refresh()
742
{
743
    DECL_TRACER("TButton::refresh()");
744
 
745
    makeElement();
746
}
747
 
15 andreas 748
bool TButton::makeElement(int instance)
749
{
750
    DECL_TRACER("TButton::makeElement()");
751
 
33 andreas 752
    if (prg_stopped)
753
        return false;
35 andreas 754
 
15 andreas 755
    int inst = mActInstance;
756
 
46 andreas 757
    if (instance >= 0 && (size_t)instance < sr.size())
15 andreas 758
        inst = instance;
759
 
760
    if (type == MULTISTATE_GENERAL && ar == 1)
761
        return drawButtonMultistateAni();
762
    else if (type == BARGRAPH)
763
        return drawBargraph(inst, mLastLevel);
38 andreas 764
    else if (type == MULTISTATE_BARGRAPH)
46 andreas 765
        return drawMultistateBargraph(mLastLevel, true);
50 andreas 766
    else if (type == TEXT_INPUT)
767
    {
51 andreas 768
        if (isSystemButton() && !mSystemReg)
769
            registerSystemButton();
770
 
50 andreas 771
        drawTextArea(mActInstance);
772
    }
100 andreas 773
    else if (isSystemButton() && ch == 17)  // System button sound ON/OFF
71 andreas 774
    {
775
        if (TConfig::getSystemSoundState())
776
            inst = mActInstance = 1;
777
        else
778
            inst = mActInstance = 0;
779
 
780
        return drawButton(inst);
781
    }
15 andreas 782
    else
783
        return drawButton(inst);
784
 
785
    return false;
786
}
787
 
14 andreas 788
bool TButton::setActive(int instance)
789
{
790
    DECL_TRACER("TButton::setActive(int instance)");
791
 
53 andreas 792
    if (mAniRunning)
793
        return true;
794
 
14 andreas 795
    if ((size_t)instance >= sr.size())
796
    {
797
        MSG_ERROR("Instance " << instance << " is higher than the maximum instance " << sr.size() << "!");
798
        return false;
799
    }
800
 
801
    if (instance == mActInstance)
802
    {
803
        MSG_TRACE("Not necessary to set instance " << instance << " again.");
804
        return true;
805
    }
806
 
807
    mActInstance = instance;
15 andreas 808
    makeElement(instance);
14 andreas 809
 
810
    return true;
811
}
812
 
813
bool TButton::setIcon(int id, int instance)
814
{
815
    DECL_TRACER("TButton::setIcon(int id, int instance)");
816
 
817
    if ((size_t)instance >= sr.size())
818
    {
819
        MSG_ERROR("Instance " << instance << " does not exist!");
820
        return false;
821
    }
822
 
823
    sr[instance].ii = id;
16 andreas 824
    return makeElement(instance);
14 andreas 825
}
826
 
827
bool TButton::setIcon(const string& icon, int instance)
828
{
829
    DECL_TRACER("TButton::setIcon(const string& icon, int instance)");
830
 
831
    if ((size_t)instance >= sr.size())
832
    {
833
        MSG_ERROR("Instance " << instance << " does not exist!");
834
        return false;
835
    }
836
 
837
    if (!gIcons)
838
    {
839
        gIcons = new TIcons();
840
 
841
        if (TError::isError())
842
        {
843
            MSG_ERROR("Error initializing icons!");
844
            return false;
845
        }
846
    }
847
 
848
    int id = gIcons->getNumber(icon);
849
 
850
    if (id == -1)
851
    {
852
        MSG_WARNING("Icon " << icon << " not found!");
853
        return false;
854
    }
855
 
856
    sr[instance].ii = id;
16 andreas 857
    return makeElement(instance);
14 andreas 858
}
859
 
860
bool TButton::revokeIcon(int instance)
861
{
862
    DECL_TRACER("TButton::revokeIcon(int instance)");
863
 
864
    if ((size_t)instance >= sr.size())
865
    {
866
        MSG_ERROR("Instance " << instance << " does not exist!");
867
        return false;
868
    }
869
 
870
    sr[instance].ii = 0;
16 andreas 871
    return makeElement(instance);
14 andreas 872
}
873
 
874
bool TButton::setText(const string& txt, int instance)
875
{
876
    DECL_TRACER("TButton::setText(const string& txt, int instance)");
877
 
878
    if ((size_t)instance >= sr.size())
879
    {
880
        MSG_ERROR("Instance " << instance << " does not exist!");
881
        return false;
882
    }
883
 
884
    sr[instance].te = txt;
16 andreas 885
    return makeElement(instance);
14 andreas 886
}
887
 
51 andreas 888
bool TButton::setTextOnly(const string& txt, int instance)
889
{
890
    DECL_TRACER("TButton::setTextOnly(const string& txt, int instance)");
891
 
892
    if ((size_t)instance >= sr.size())
893
    {
894
        MSG_ERROR("Instance " << instance << " does not exist!");
895
        return false;
896
    }
897
 
898
    sr[instance].te = txt;
899
    return true;
900
}
901
 
43 andreas 902
bool TButton::appendText(const string &txt, int instance)
903
{
904
    DECL_TRACER("TButton::appendText(const string &txt, int instance)");
905
 
906
    if ((size_t)instance >= sr.size())
907
    {
60 andreas 908
        MSG_ERROR("Instance " << instance << " does not exist!");
43 andreas 909
        return false;
910
    }
911
 
912
    sr[instance].te.append(txt);
913
    return makeElement(instance);
914
}
915
 
916
bool TButton::setBorderColor(const string &color, int instance)
917
{
918
    DECL_TRACER("TButton::setBorderColor(const string &color, int instance)");
919
 
920
    if ((size_t)instance >= sr.size())
921
    {
60 andreas 922
        MSG_ERROR("Instance " << instance << " does not exist!");
43 andreas 923
        return false;
924
    }
925
 
926
    if (sr[instance].cb.compare(color) == 0)
927
        return true;
928
 
929
    sr[instance].cb = color;
930
    return makeElement(instance);
931
}
932
 
82 andreas 933
string TButton::getBorderColor(int instance)
934
{
935
    DECL_TRACER("TButton::getBorderColor(int instance)");
936
 
937
    if ((size_t)instance >= sr.size())
938
    {
939
        MSG_ERROR("Instance " << instance << " does not exist!");
940
        return string();
941
    }
942
 
943
    return sr[instance].cb;
944
}
945
 
60 andreas 946
bool TButton::setFillColor(const string& color, int instance)
947
{
948
    DECL_TRACER("TButton::setFillColor(const string& color, int instance)");
949
 
950
    if ((size_t)instance >= sr.size())
951
    {
952
        MSG_ERROR("Instance " << instance << " does not exist!");
953
        return false;
954
    }
955
 
956
    if (sr[instance].cf.compare(color) == 0)
957
        return true;
958
 
959
    sr[instance].cf = color;
960
    return makeElement(instance);
961
}
962
 
963
bool TButton::setTextColor(const string& color, int instance)
964
{
965
    DECL_TRACER("TButton::setTextColor(const string& color, int instance)");
966
 
967
    if ((size_t)instance >= sr.size())
968
    {
969
        MSG_ERROR("Instance " << instance << " does not exist!");
970
        return false;
971
    }
972
 
973
    if (sr[instance].ct.compare(color) == 0)
974
        return true;
975
 
976
    sr[instance].ct = color;
977
    return makeElement(instance);
978
}
979
 
980
bool TButton::setDrawOrder(const string& order, int instance)
981
{
982
    DECL_TRACER("TButton::setDrawOrder(const string& order, int instance)");
983
 
984
    if ((size_t)instance >= sr.size())
985
    {
986
        MSG_ERROR("Instance " << instance << " does not exist!");
987
        return false;
988
    }
989
 
990
    if (sr[instance]._do.compare(order) == 0)
991
        return true;
992
 
993
    sr[instance]._do = order;
994
    return makeElement(instance);
995
}
996
 
997
bool TButton::setFeedback(FEEDBACK feedback)
998
{
999
    DECL_TRACER("TButton::setFeedback(FEEDBACK feedback)");
1000
 
1001
    fb = feedback;
1002
    return true;
1003
}
1004
 
1005
bool TButton::setBorderStyle(const string& style, int instance)
1006
{
1007
    DECL_TRACER("TButton::setBorderStyle(const string& style, int instance)");
1008
 
106 andreas 1009
    if (instance == 0 || (size_t)instance >= sr.size())
60 andreas 1010
    {
1011
        MSG_ERROR("Instance " << instance << " does not exist!");
1012
        return false;
1013
    }
1014
 
106 andreas 1015
    int inst = (instance > 0) ? (instance - 1) : instance;
1016
 
60 andreas 1017
    if (strCaseCompare(style, "None") == 0)     // Clear the border?
1018
    {
106 andreas 1019
        if (inst < 0)
60 andreas 1020
            bs.clear();
1021
        else
106 andreas 1022
            sr[inst].bs.clear();
60 andreas 1023
 
1024
        return true;
1025
    }
1026
 
79 andreas 1027
    // Look in the system table and try to find the border.
1028
    if (gPageManager && gPageManager->getSystemDraw())
1029
    {
1030
        if (gPageManager->getSystemDraw()->existBorder(style))
1031
        {
106 andreas 1032
            if (inst < 0)
79 andreas 1033
                bs = style;
1034
            else
106 andreas 1035
                sr[inst].bs = style;
79 andreas 1036
 
1037
            return true;
1038
        }
1039
    }
1040
 
60 andreas 1041
    // Check whether it is a supported style or not. If the style is not
1042
    // supported, it will be ignored.
1043
    int i = 0;
1044
 
1045
    while (sysBorders[i].id > 0)
1046
    {
1047
        if (strCaseCompare(sysBorders[i].name, style) == 0)
1048
        {
106 andreas 1049
            if (inst < 0)
60 andreas 1050
                bs = sysBorders[i].name;
1051
            else
106 andreas 1052
                sr[inst].bs = sysBorders[i].name;
60 andreas 1053
 
1054
            return true;
1055
        }
1056
 
1057
        i++;
1058
    }
1059
 
1060
    return false;
1061
}
1062
 
106 andreas 1063
string TButton::getBorderStyle(int instance)
1064
{
1065
    DECL_TRACER("TButton::getBorderStyle(int instance)");
1066
 
1067
    if (instance < 0 || instance >= (int)sr.size())
1068
    {
1069
        MSG_ERROR("Invalid instance " << (instance + 1) << " submitted!");
1070
        return string();
1071
    }
1072
 
1073
    return sr[instance].bs;
1074
}
1075
 
60 andreas 1076
bool TButton::setBargraphUpperLimit(int limit)
1077
{
1078
    DECL_TRACER("TButton::setBargraphUpperLimit(int limit)");
1079
 
1080
    if (limit < 1 || limit > 65535)
1081
    {
1082
        MSG_ERROR("Invalid upper limit " << limit);
1083
        return false;
1084
    }
1085
 
1086
    rh = limit;
1087
    return true;
1088
}
1089
 
1090
bool TButton::setBargraphLowerLimit(int limit)
1091
{
1092
    DECL_TRACER("TButton::setBargraphLowerLimit(int limit)");
1093
 
1094
    if (limit < 1 || limit > 65535)
1095
    {
1096
        MSG_ERROR("Invalid lower limit " << limit);
1097
        return false;
1098
    }
1099
 
1100
    rl = limit;
1101
    return true;
1102
}
1103
 
108 andreas 1104
bool TButton::setBargraphSliderColor(const string& color)
1105
{
1106
    DECL_TRACER("TButton::setBargraphSliderColor(const string& color, int inst)");
1107
 
1108
    if (!TColor::isValidAMXcolor(color))
1109
    {
1110
        MSG_PROTOCOL("Invalid color >" << color << "< ignored!");
1111
        return false;
1112
    }
1113
 
1114
    sc = color;
1115
 
1116
    if (visible)
1117
        refresh();
1118
 
1119
    return true;
1120
}
1121
 
16 andreas 1122
bool TButton::setBitmap(const string& file, int instance)
1123
{
1124
    DECL_TRACER("TButton::setBitmap(const string& file, int instance)");
1125
 
1126
    if (file.empty() || instance >= (int)sr.size())
1127
    {
1128
        MSG_ERROR("Invalid parameters!");
1129
        return false;
1130
    }
1131
 
104 andreas 1132
    if (sr[instance].bm == file)
1133
        return true;
1134
 
16 andreas 1135
    sr[instance].bm = file;
1136
    map<int, IMAGE_t>::iterator iter = mImages.find(sr[instance].number);
1137
 
1138
    if (iter != mImages.end())
1139
        mImages.erase(iter);
1140
 
40 andreas 1141
    if (!createButtons(true))   // We're forcing the image to load
16 andreas 1142
        return false;
1143
 
1144
    return makeElement(instance);
1145
}
1146
 
104 andreas 1147
bool TButton::setCameleon(const string& file, int instance)
1148
{
1149
    DECL_TRACER("TButton::setCameleon(const string& file, int instance)");
1150
 
1151
    if (file.empty() || instance >= (int)sr.size())
1152
    {
1153
        MSG_ERROR("Invalid parameters!");
1154
        return false;
1155
    }
1156
 
1157
    if (sr[instance].mi == file)
1158
        return true;
1159
 
1160
    sr[instance].mi = file;
1161
 
1162
    if (!createButtons(true))   // We're forcing the image to load
1163
        return false;
1164
 
1165
    return makeElement(instance);
1166
}
1167
 
51 andreas 1168
void TButton::setActiveInstance(int inst)
1169
{
1170
    DECL_TRACER("TButton::setActiveInstance()");
1171
 
1172
    if (inst < 0 || (size_t)inst >= sr.size())
1173
        return;
1174
 
1175
    mActInstance = inst;
1176
}
1177
 
110 andreas 1178
bool TButton::getDynamic(int inst)
1179
{
1180
    DECL_TRACER("TButton::getDynamic(int inst)");
1181
 
1182
    if (inst < 0 || inst >= (int)sr.size())
1183
    {
1184
        MSG_ERROR("Instance " << inst << " does not exist!");
1185
        return false;
1186
    }
1187
 
1188
    return sr[inst].dynamic;
1189
}
1190
 
107 andreas 1191
void TButton::setDynamic(int d, int inst)
1192
{
1193
    DECL_TRACER("TButton::setDynamic(int d, int inst)");
1194
 
1195
    if (inst >= (int)sr.size())
1196
    {
1197
        MSG_ERROR("Instance is out of size!");
1198
        return;
1199
    }
1200
 
1201
    bool dyn = (d != 0) ? true : false;
1202
 
1203
    if (inst < 0)
1204
    {
1205
        vector<SR_T>::iterator iter;
1206
        int instance = 0;
1207
 
1208
        for (iter = sr.begin(); iter != sr.end(); ++iter)
1209
        {
1210
            bool old = iter->dynamic;
1211
            iter->dynamic = dyn;
1212
 
1213
            if (old && old != dyn && mActInstance == instance)
1214
            {
1215
                THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
1216
 
1217
                if (thref)
1218
                {
1219
                    TImageRefresh *mImageRefresh = thref->mImageRefresh;
1220
 
1221
                    if (mImageRefresh)
1222
                        mImageRefresh->stop();
1223
                }
1224
 
1225
                makeElement(instance);
1226
            }
1227
 
1228
            instance++;
1229
        }
1230
    }
1231
    else
1232
    {
1233
        bool old = sr[inst].dynamic;
1234
        sr[inst].dynamic = dyn;
1235
 
1236
        if (old && old != dyn && mActInstance == inst)
1237
        {
1238
            THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
1239
 
1240
            if (thref)
1241
            {
1242
                TImageRefresh *mImageRefresh = thref->mImageRefresh;
1243
 
1244
                if (mImageRefresh)
1245
                    mImageRefresh->stop();
1246
            }
1247
 
1248
            makeElement(inst);
1249
        }
1250
    }
1251
}
1252
 
110 andreas 1253
int TButton::getOpacity(int inst)
1254
{
1255
    DECL_TRACER("TButoon::getOpacity(int inst)");
1256
 
1257
    if (inst < 0 || inst >= (int)sr.size())
1258
    {
1259
        MSG_ERROR("Instance " << inst << " does not exist!");
1260
        return 0;
1261
    }
1262
 
1263
    return sr[inst].oo;
1264
}
1265
 
16 andreas 1266
bool TButton::setOpacity(int op, int instance)
1267
{
1268
    DECL_TRACER("TButton::setOpacity(int op, int instance)");
1269
 
1270
    if ((size_t)instance >= sr.size())
1271
    {
1272
        MSG_ERROR("Instance " << instance << " does not exist!");
1273
        return false;
1274
    }
1275
 
1276
    if (op < 0 || op < 255)
1277
    {
1278
        MSG_ERROR("Invalid opacity " << op << "!");
1279
        return false;
1280
    }
1281
 
1282
    sr[instance].oo = op;
1283
    return makeElement(instance);
1284
}
1285
 
1286
bool TButton::setFont(int id, int instance)
1287
{
1288
    DECL_TRACER("TButton::setFont(int id)");
1289
 
1290
    if (instance < 0 || (size_t)instance >= sr.size())
1291
    {
1292
        MSG_ERROR("Instance " << instance << " does not exist!");
1293
        return false;
1294
    }
1295
 
1296
    sr[instance].fi = id;
1297
    return makeElement(instance);
1298
}
1299
 
1300
void TButton::setLeft(int left)
1301
{
1302
    DECL_TRACER("TButton::setLeft(int left)");
1303
 
1304
    if (left < 0)
1305
        return;
1306
 
1307
    lt = left;
1308
    makeElement(mActInstance);
1309
}
1310
 
1311
void TButton::setTop(int top)
1312
{
1313
    DECL_TRACER("TButton::setTop(int top)");
1314
 
1315
    if (top < 0)
1316
        return;
1317
 
1318
    tp = top;
1319
    makeElement(mActInstance);
1320
}
1321
 
1322
void TButton::setLeftTop(int left, int top)
1323
{
1324
    DECL_TRACER("TButton::setLeftTop(int left, int top)");
1325
 
1326
    if (top < 0 || left < 0)
1327
        return;
1328
 
1329
    lt = left;
1330
    tp = top;
1331
    makeElement(mActInstance);
1332
}
1333
 
21 andreas 1334
void TButton::setResourceName(const string& name, int instance)
1335
{
1336
    DECL_TRACER("TButton::setResourceName(const string& name, int instance)");
1337
 
1338
    if (instance < 0 || instance >= (int)sr.size())
1339
    {
1340
        MSG_ERROR("Invalid instance " << instance);
1341
        return;
1342
    }
1343
 
1344
    if (!sr[instance].dynamic)
1345
    {
1346
        MSG_ERROR("Button " << bi << ": " << na << " is not a resource button!");
1347
        return;
1348
    }
1349
 
1350
    sr[instance].bm = name;
1351
}
1352
 
106 andreas 1353
int TButton::getBitmapJustification(int* x, int* y, int instance)
104 andreas 1354
{
106 andreas 1355
    DECL_TRACER("TButton::getBitmapJustification(int* x, int* y, int instance)");
104 andreas 1356
 
106 andreas 1357
    if (instance < 0 || instance >= (int)sr.size())
1358
    {
1359
        MSG_ERROR("Invalid instance " << (instance + 1));
1360
        return -1;
1361
    }
1362
 
1363
    if (x)
1364
        *x = sr[instance].bx;
1365
 
1366
    if (y)
1367
        *y = sr[instance].by;
1368
 
1369
    return sr[instance].jb;
1370
}
1371
 
1372
void TButton::setBitmapJustification(int j, int x, int y, int instance)
1373
{
1374
    DECL_TRACER("TButton::setBitmapJustification(int j, int instance)");
1375
 
104 andreas 1376
    if (j < 0 || j > 9 || instance < 0 || instance >= (int)sr.size())
1377
        return;
1378
 
106 andreas 1379
    if (instance < 0)
104 andreas 1380
    {
1381
        for (size_t i = 0; i < sr.size(); i++)
1382
        {
1383
            sr[i].jb = j;
1384
 
1385
            if (j == 0)
1386
            {
1387
                sr[i].bx = x;
1388
                sr[i].by = y;
1389
            }
1390
        }
1391
    }
1392
    else
1393
    {
1394
        sr[instance].jb = j;
1395
 
1396
        if (j == 0)
1397
        {
1398
            sr[instance].bx = x;
1399
            sr[instance].by = y;
1400
        }
1401
    }
110 andreas 1402
 
1403
    makeElement();
104 andreas 1404
}
1405
 
106 andreas 1406
int TButton::getIconJustification(int* x, int* y, int instance)
1407
{
1408
    DECL_TRACER("TButton::getIconJustification(int* x, int* y, int instance)");
1409
 
1410
    if (instance < 0 || instance >= (int)sr.size())
1411
    {
1412
        MSG_ERROR("Invalid instance " << (instance + 1));
1413
        return -1;
1414
    }
1415
 
1416
    if (x)
1417
        *x = sr[instance].ix;
1418
 
1419
    if (y)
1420
        *y = sr[instance].iy;
1421
 
1422
    return sr[instance].ji;
1423
}
1424
 
104 andreas 1425
void TButton::setIconJustification(int j, int x, int y, int instance)
1426
{
1427
    DECL_TRACER("TButton::setIconJustification(int j, int x, int y, int instance)");
1428
 
1429
    if (j < 0 || j > 9 || instance < 0 || instance >= (int)sr.size())
1430
        return;
1431
 
106 andreas 1432
    if (instance < 0)
104 andreas 1433
    {
1434
        for (size_t i = 0; i < sr.size(); i++)
1435
        {
1436
            sr[i].ji = j;
1437
 
1438
            if (j == 0)
1439
            {
1440
                sr[i].ix = x;
1441
                sr[i].iy = y;
1442
            }
1443
        }
1444
    }
1445
    else
1446
    {
1447
        sr[instance].ji = j;
1448
 
1449
        if (j == 0)
1450
        {
1451
            sr[instance].ix = x;
1452
            sr[instance].iy = y;
1453
        }
1454
    }
110 andreas 1455
 
1456
    makeElement();
104 andreas 1457
}
1458
 
106 andreas 1459
int TButton::getTextJustification(int* x, int* y, int instance)
1460
{
1461
    DECL_TRACER("TButton::getTextJustification(int* x, int* y, int instance)");
1462
 
1463
    if (instance < 0 || instance >= (int)sr.size())
1464
    {
1465
        MSG_ERROR("Invalid instance " << (instance + 1));
1466
        return -1;
1467
    }
1468
 
1469
    if (x)
1470
        *x = sr[instance].tx;
1471
 
1472
    if (y)
1473
        *y = sr[instance].ty;
1474
 
1475
    return sr[instance].jt;
1476
}
1477
 
104 andreas 1478
void TButton::setTextJustification(int j, int x, int y, int instance)
1479
{
1480
    DECL_TRACER("TButton::setTextJustification(int j, int x, int y, int instance)");
1481
 
1482
    if (j < 0 || j > 9 || instance < 0 || instance >= (int)sr.size())
1483
        return;
1484
 
106 andreas 1485
    if (instance < 0)
104 andreas 1486
    {
1487
        for (size_t i = 0; i < sr.size(); i++)
1488
        {
1489
            sr[i].jt = (TEXT_ORIENTATION)j;
1490
 
1491
            if (j == 0)
1492
            {
1493
                sr[i].tx = x;
1494
                sr[i].ty = y;
1495
            }
1496
        }
1497
    }
1498
    else
1499
    {
1500
        sr[instance].jt = (TEXT_ORIENTATION)j;
1501
 
1502
        if (j == 0)
1503
        {
1504
            sr[instance].tx = x;
1505
            sr[instance].ty = y;
1506
        }
1507
    }
110 andreas 1508
 
1509
    makeElement();
104 andreas 1510
}
1511
 
110 andreas 1512
string TButton::getText(int inst)
1513
{
1514
    DECL_TRACER("TButton::getText(int inst)");
1515
 
1516
    if (inst < 0 || inst >= (int)sr.size())
1517
    {
1518
        MSG_ERROR("Instance " << inst << " does not exist!");
1519
        return string();
1520
    }
1521
 
1522
    return sr[inst].te;
1523
}
1524
 
1525
string TButton::getTextColor(int inst)
1526
{
1527
    DECL_TRACER("TButton::getTextColor(int const)");
1528
 
1529
    if (inst < 0 || inst >= (int)sr.size())
1530
    {
1531
        MSG_ERROR("Instance " << inst << " does not exist!");
1532
        return string();
1533
    }
1534
 
1535
    return sr[inst].ct;
1536
}
1537
 
1538
string TButton::getTextEffectColor(int inst)
1539
{
1540
    DECL_TRACER ("TButton::getTextEffectColor(int inst)");
1541
 
1542
    if (inst < 0 || inst >= (int)sr.size())
1543
    {
1544
        MSG_ERROR("Instance " << inst << " does not exist!");
1545
        return string();
1546
    }
1547
 
1548
    return sr[inst].ec;
1549
}
1550
 
108 andreas 1551
void TButton::setTextEffectColor(const string& ec, int inst)
1552
{
1553
    DECL_TRACER("TButton::setTextEffectColor(const string& ec, int inst)");
1554
 
1555
    if ((size_t)inst >= sr.size())
1556
    {
1557
        MSG_ERROR("Instance " << inst << " does not exist!");
1558
        return;
1559
    }
1560
 
1561
    if (!TColor::isValidAMXcolor(ec))
1562
    {
1563
        MSG_PROTOCOL("Invalid color >" << ec << "< ignored!");
1564
        return;
1565
    }
1566
 
1567
    if (sr[inst].ec.compare(ec) == 0)
1568
        return;
1569
 
1570
    if (inst < 0)
1571
    {
1572
        for (size_t i = 0; i < sr.size(); i++)
1573
            sr[i].ec = ec;
1574
    }
1575
    else
1576
        sr[inst].ec = ec;
1577
 
1578
    if (visible)
110 andreas 1579
        makeElement();
108 andreas 1580
}
1581
 
110 andreas 1582
int TButton::getTextEffect(int inst)
16 andreas 1583
{
110 andreas 1584
    DECL_TRACER("TButton::getTextEffect(int inst)");
1585
 
1586
    if (inst < 0 || inst >= (int)sr.size())
1587
    {
1588
        MSG_ERROR("Instance " << inst << " does not exist!");
1589
        return 0;
1590
    }
1591
 
1592
    return sr[inst].et;
1593
}
1594
 
1595
void TButton::setTextEffect(int et, int inst)
1596
{
1597
    DECL_TRACER("TButton::setTextEffect(bool et, int inst)");
1598
 
1599
    if (inst >= (int)sr.size())
1600
    {
1601
        MSG_ERROR("instance " << inst << " is out of bounds!");
1602
        return;
1603
    }
1604
 
1605
    if (inst < 0)
1606
    {
1607
        for (size_t i = 0; i < sr.size(); i++)
1608
            sr[i].et = et;
1609
    }
1610
    else
1611
        sr[inst].et = et;
1612
 
1613
    makeElement();
1614
}
1615
 
1616
string TButton::getTextEffectName(int inst)
1617
{
1618
    DECL_TRACER("TButton::getTextEffectName(int inst)");
1619
 
1620
    if (inst < 0 || inst >= (int)sr.size())
1621
        return string();
1622
 
1623
    int idx = 0;
1624
 
1625
    while (sysTefs[idx].idx)
1626
    {
1627
        if (sysTefs[idx].idx == sr[inst].et)
1628
            return sysTefs[idx].name;
1629
 
1630
        idx++;
1631
    }
1632
 
1633
    return string();
1634
}
1635
 
1636
void TButton::setTextEffectName(const string& name, int inst)
1637
{
1638
    DECL_TRACER("TButton::setTextEffectName(const string& name, int inst)");
1639
 
1640
    if (inst >= (int)sr.size())
1641
        return;
1642
 
1643
    int idx = 0;
1644
 
1645
    while (sysTefs[idx].idx)
1646
    {
1647
        if (strCaseCompare(sysTefs[idx].name, name) == 0)
1648
        {
1649
            if (inst < 0)
1650
            {
1651
                for (size_t i = 0; i < sr.size(); i++)
1652
                    sr[i].et = sysTefs[idx].idx;
1653
            }
1654
            else
1655
                sr[inst].et = sysTefs[idx].idx;
1656
 
1657
            makeElement();
1658
            break;
1659
        }
1660
 
1661
        idx++;
1662
    }
1663
}
1664
 
1665
string TButton::getBitmapName(int inst)
1666
{
1667
    DECL_TRACER("TButton::getBitmapName(int inst)");
1668
 
1669
    if (inst < 0 || inst >= (int)sr.size())
1670
    {
1671
        MSG_ERROR("Instance " << inst << " does not exist!");
1672
        return string();
1673
    }
1674
 
1675
    return sr[inst].bm;
1676
}
1677
 
1678
string TButton::getFillColor(int inst)
1679
{
1680
    DECL_TRACER("TButton::getFillColor(int inst)");
1681
 
1682
    if (inst < 0 || inst >= (int)sr.size())
1683
    {
1684
        MSG_ERROR("Instance " << inst << " does not exist!");
1685
        return string();
1686
    }
1687
 
1688
    return sr[inst].cf;
1689
}
1690
 
1691
bool TButton::setTextWordWrap(bool state, int instance)
1692
{
16 andreas 1693
    DECL_TRACER("TButton::setWorWrap(bool state, int instance)");
1694
 
110 andreas 1695
    if (instance >= (int)sr.size())
16 andreas 1696
    {
1697
        MSG_ERROR("Invalid instance " << instance);
1698
        return false;
1699
    }
1700
 
110 andreas 1701
    if (instance < 0)
1702
    {
1703
        for (size_t i = 0; i < sr.size(); i++)
1704
            sr[i].ww = state ? 1 : 0;
1705
    }
1706
    else
1707
        sr[instance].ww = state ? 1 : 0;
1708
 
16 andreas 1709
    return makeElement(instance);
1710
}
1711
 
110 andreas 1712
bool TButton::getTextWordWrap(int inst)
1713
{
1714
    DECL_TRACER("TButton::getTextWordWrap(int inst)");
1715
 
1716
    if (inst < 0 || inst >= (int)sr.size())
1717
    {
1718
        MSG_ERROR("Instance " << inst << " does not exist!");
1719
        return false;
1720
    }
1721
 
1722
    return (sr[inst].ww == 1);
1723
}
1724
 
1725
int TButton::getFontIndex(int inst)
1726
{
1727
    DECL_TRACER("TButton::getFontIndex(int inst)");
1728
 
1729
    if (inst < 0 || inst >= (int)sr.size())
1730
    {
1731
        MSG_ERROR("Instance " << inst << " does not exist!");
1732
        return 0;
1733
    }
1734
 
1735
    return sr[inst].fi;
1736
}
1737
 
1738
bool TButton::setFontIndex(int fi, int inst)
1739
{
1740
    DECL_TRACER("TButton::setFontIndex(int fi, int inst)");
1741
 
1742
    if (inst >= (int)sr.size())
1743
    {
1744
        MSG_ERROR("Invalid instance " << inst);
1745
        return false;
1746
    }
1747
 
1748
    if (inst < 0)
1749
    {
1750
        for (size_t i = 0; i < sr.size(); i++)
1751
            sr[i].fi = fi;
1752
    }
1753
    else
1754
        sr[inst].fi = fi;
1755
 
1756
    return makeElement(inst);
1757
}
1758
 
1759
int TButton::getIconIndex(int inst)
1760
{
1761
    DECL_TRACER("TButton::getIconIndex(int inst)");
1762
 
1763
    if (inst < 0 || inst >= (int)sr.size())
1764
    {
1765
        MSG_ERROR("Instance " << inst << " does not exist!");
1766
        return 0;
1767
    }
1768
 
1769
    return sr[inst].ii;
1770
}
1771
 
1772
string TButton::getSound(int inst)
1773
{
1774
    DECL_TRACER("TButton::getSound(int inst)");
1775
 
1776
    if (inst < 0 || inst >= (int)sr.size())
1777
    {
1778
        MSG_ERROR("Instance " << inst << " does not exist!");
1779
        return string();
1780
    }
1781
 
1782
    return sr[inst].sd;
1783
}
1784
 
1785
void TButton::setSound(const string& sound, int inst)
1786
{
1787
    DECL_TRACER("TButton::setSound(const string& sound, int inst)");
1788
 
1789
    if (inst >= (int)sr.size())
1790
    {
1791
        MSG_ERROR("Invalid instance " << inst);
1792
        return;
1793
    }
1794
 
1795
    if (inst < 0)
1796
    {
1797
        for (size_t i = 0; i < sr.size(); i++)
1798
            sr[i].sd = sound;
1799
    }
1800
    else
1801
        sr[inst].sd = sound;
1802
}
1803
 
38 andreas 1804
bool TButton::startAnimation(int start, int end, int time)
1805
{
1806
    DECL_TRACER("TButton::startAnimation(int start, int end, int time)");
1807
 
1808
    if (start >= end || start < 0 || (size_t)end >= sr.size() || time <= 0)
1809
    {
1810
        MSG_ERROR("Invalid parameter: start=" << start << ", end=" << end << ", time=" << time);
1811
        return false;
1812
    }
1813
 
1814
    if (mAniRunning || mThrAni.joinable())
1815
    {
99 andreas 1816
        MSG_PROTOCOL("Animation is already running!");
38 andreas 1817
        return true;
1818
    }
1819
 
1820
    int number = end - start;
1821
    ulong stepTime = ((ulong)time * 10L) / (ulong)number;
1822
    mAniRunTime = (ulong)time * 10L;
1823
 
1824
    try
1825
    {
93 andreas 1826
        mAniStop = false;
38 andreas 1827
        mThrAni = thread([=] { runAnimationRange(start, end, stepTime); });
1828
        mThrAni.detach();
1829
    }
1830
    catch (exception& e)
1831
    {
1832
        MSG_ERROR("Error starting the button animation thread: " << e.what());
1833
        return false;
1834
    }
1835
 
1836
    return true;
1837
}
1838
 
22 andreas 1839
void TButton::_TimerCallback(ulong)
15 andreas 1840
{
1841
    mLastBlink.second++;
1842
    int months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1843
 
1844
    if ((mLastBlink.year % 4) == 0)
1845
        months[1] = 29;
1846
 
1847
    if (mLastBlink.second > 59)
1848
    {
1849
        mLastBlink.minute++;
1850
        mLastBlink.second = 0;
1851
 
1852
        if (mLastBlink.minute > 59)
1853
        {
1854
            mLastBlink.hour++;
1855
            mLastBlink.minute = 0;
1856
 
1857
            if (mLastBlink.hour >= 24)
1858
            {
1859
                mLastBlink.hour = 0;
1860
                mLastBlink.weekday++;
1861
                mLastBlink.day++;
1862
 
1863
                if (mLastBlink.weekday > 7)
1864
                    mLastBlink.weekday = 0;
1865
 
1866
                if (mLastBlink.day > months[mLastBlink.month-1])
1867
                {
1868
                    mLastBlink.day = 1;
1869
                    mLastBlink.month++;
1870
 
1871
                    if (mLastBlink.month > 12)
1872
                    {
1873
                        mLastBlink.year++;
1874
                        mLastBlink.month = 1;
1875
                    }
1876
                }
1877
            }
1878
        }
1879
    }
1880
 
1881
    funcTimer(mLastBlink);
1882
}
1883
 
21 andreas 1884
void TButton::_imageRefresh(const string& url)
1885
{
1886
    DECL_TRACER("TButton::_imageRefresh(const string& url)");
1887
 
38 andreas 1888
    if (prg_stopped || killed || !visible)
33 andreas 1889
        return;
35 andreas 1890
 
1891
    if (!gPrjResources)
1892
    {
1893
        MSG_WARNING("No resources available!");
1894
        return;
1895
    }
1896
 
21 andreas 1897
    ulong parent = mHandle & 0xffff0000;
1898
    getDrawOrder(sr[mActInstance]._do, (DRAW_ORDER *)&mDOrder);
1899
 
1900
    if (TError::isError())
1901
    {
1902
        TError::clear();
1903
        return;
1904
    }
1905
 
1906
    SkBitmap imgButton;
1907
    imgButton.allocN32Pixels(wt, ht);
1908
 
1909
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
1910
    {
1911
        if (mDOrder[i] == ORD_ELEM_FILL)
1912
        {
1913
            if (!buttonFill(&imgButton, mActInstance))
1914
                return;
1915
        }
1916
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
1917
        {
1918
            RESOURCE_T resource = gPrjResources->findResource(sr[mActInstance].bm);
1919
 
1920
            if (resource.protocol.empty())
1921
            {
1922
                MSG_ERROR("Resource " << sr[mActInstance].bm << " not found!");
1923
                return;
1924
            }
1925
 
94 andreas 1926
            THTTPClient *WEBClient = nullptr;
1927
 
21 andreas 1928
            try
1929
            {
1930
                char *content = nullptr;
1931
                size_t length = 0, contentlen = 0;
1932
 
94 andreas 1933
                WEBClient = new THTTPClient;
1934
 
1935
                if (WEBClient && (content = WEBClient->tcall(&length, url, resource.user, resource.password)) == nullptr)
1936
                {
1937
                    if (WEBClient)
1938
                        delete WEBClient;
1939
 
21 andreas 1940
                    return;
94 andreas 1941
                }
21 andreas 1942
 
94 andreas 1943
                contentlen = WEBClient->getContentSize();
21 andreas 1944
 
1945
                if (content == nullptr)
1946
                {
1947
                    MSG_ERROR("Server returned no or invalid content!");
94 andreas 1948
                    delete WEBClient;
21 andreas 1949
                    return;
1950
                }
1951
 
1952
                sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
1953
 
1954
                if (!data)
1955
                {
1956
                    MSG_ERROR("Could not make an image!");
94 andreas 1957
                    delete WEBClient;
21 andreas 1958
                    return;
1959
                }
1960
 
1961
                SkBitmap image;
1962
 
1963
                if (!DecodeDataToBitmap(data, &image))
1964
                {
1965
                    MSG_ERROR("Error creating an image!");
94 andreas 1966
                    delete WEBClient;
21 andreas 1967
                    return;
1968
                }
1969
 
1970
                loadImage(&imgButton, image, mActInstance);
94 andreas 1971
                delete WEBClient;
21 andreas 1972
            }
1973
            catch (std::exception& e)
1974
            {
94 andreas 1975
                if (WEBClient)
1976
                    delete WEBClient;
1977
 
21 andreas 1978
                MSG_ERROR(e.what());
1979
                return;
1980
            }
93 andreas 1981
            catch(...)
1982
            {
94 andreas 1983
                if (WEBClient)
1984
                    delete WEBClient;
1985
 
93 andreas 1986
                MSG_ERROR("Unexpected exception occured. [TButton::_imageRefresh()]");
1987
                return;
1988
            }
21 andreas 1989
        }
1990
        else if (mDOrder[i] == ORD_ELEM_ICON)
1991
        {
1992
            if (!buttonIcon(&imgButton, mActInstance))
1993
                return;
1994
        }
1995
        else if (mDOrder[i] == ORD_ELEM_TEXT)
1996
        {
1997
            if (!buttonText(&imgButton, mActInstance))
1998
                return;
1999
        }
2000
        else if (mDOrder[i] == ORD_ELEM_BORDER)
2001
        {
2002
            if (!buttonBorder(&imgButton, mActInstance))
2003
                return;
2004
        }
2005
    }
2006
 
2007
    if (mGlobalOO >= 0 || sr[mActInstance].oo >= 0) // Take overall opacity into consideration
2008
    {
2009
        SkBitmap ooButton;
2010
        int w = imgButton.width();
2011
        int h = imgButton.height();
2012
        ooButton.allocN32Pixels(w, h, true);
2013
        SkCanvas canvas(ooButton);
2014
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
2015
        SkRegion region;
2016
        region.setRect(irect);
2017
        SkScalar oo;
2018
 
2019
        if (mGlobalOO >= 0 && sr[mActInstance].oo >= 0)
2020
        {
2021
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[mActInstance].oo);
2022
            MSG_DEBUG("Set global overal opacity to " << oo);
2023
        }
2024
        else if (sr[mActInstance].oo >= 0)
2025
        {
2026
            oo = (SkScalar)sr[mActInstance].oo;
2027
            MSG_DEBUG("Set overal opacity to " << oo);
2028
        }
2029
        else
2030
        {
2031
            oo = (SkScalar)mGlobalOO;
2032
            MSG_DEBUG("Set global overal opacity to " << oo);
2033
        }
2034
 
2035
        SkScalar alpha = 1.0 / 255.0 * oo;
2036
        MSG_DEBUG("Calculated alpha value: " << alpha);
2037
        SkPaint paint;
2038
        paint.setAlphaf(alpha);
2039
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
2040
        canvas.drawBitmap(imgButton, 0, 0, &paint);
2041
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
2042
        imgButton = ooButton;
2043
    }
2044
 
2045
    mLastImage = imgButton;
2046
    size_t rowBytes = imgButton.info().minRowBytes();
2047
 
2048
    if (!prg_stopped && visible && _displayButton)
26 andreas 2049
    {
2050
        int rwidth = wt;
2051
        int rheight = ht;
2052
        int rleft = lt;
2053
        int rtop = tp;
43 andreas 2054
#ifdef _SCALE_SKIA_
26 andreas 2055
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
2056
        {
2057
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
2058
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
2059
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
2060
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
2061
 
2062
            SkPaint paint;
2063
            paint.setBlendMode(SkBlendMode::kSrc);
2064
            paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 2065
            // Calculate new dimension
26 andreas 2066
            SkImageInfo info = imgButton.info();
2067
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
2068
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
28 andreas 2069
            // Create a canvas and draw new image
2070
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
2071
            imgButton.allocN32Pixels(width, height);
2072
            imgButton.eraseColor(SK_ColorTRANSPARENT);
26 andreas 2073
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2074
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
2075
            can.drawImageRect(im, rect, &paint);
28 andreas 2076
            rowBytes = imgButton.info().minRowBytes();
2077
            mLastImage = imgButton;
26 andreas 2078
        }
43 andreas 2079
#endif
26 andreas 2080
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
2081
    }
21 andreas 2082
}
2083
 
15 andreas 2084
void TButton::registerSystemButton()
2085
{
2086
    DECL_TRACER("TButton::registerSystemButton()");
2087
 
2088
    if (mSystemReg)
2089
        return;
2090
 
2091
    // If this is a special system button, register it to receive the state
2092
    if (ap == 0 && ad == 8)     // Connection status?
2093
    {
2094
        MSG_TRACE("Try to register button " << na << " as connection status ...");
2095
 
2096
        if (gAmxNet)
2097
        {
2098
            gAmxNet->registerNetworkState(bind(&TButton::funcNetwork, this, std::placeholders::_1), mHandle);
2099
            mSystemReg = true;
2100
            MSG_TRACE("Button registered");
2101
        }
2102
        else
2103
            MSG_WARNING("Network class not initialized!");
2104
 
2105
    }
2106
    else if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158))) // time or date
2107
    {
2108
        MSG_TRACE("Try to register button " << na << " as time/date ...");
2109
 
2110
        if (gAmxNet)
2111
        {
2112
            gAmxNet->registerTimer(bind(&TButton::funcTimer, this, std::placeholders::_1), mHandle);
2113
            mSystemReg = true;
2114
            MSG_TRACE("Button registered");
2115
        }
2116
        else
2117
            MSG_WARNING("Network class not initialized!");
2118
 
2119
        if (ad >= 141 && ad <= 143 && !mTimer)
2120
        {
2121
            mTimer = new TTimer;
2122
            mTimer->setInterval(std::chrono::milliseconds(1000));   // 1 second
2123
            mTimer->registerCallback(bind(&TButton::_TimerCallback, this, std::placeholders::_1));
2124
            mTimer->run();
2125
        }
2126
    }
38 andreas 2127
    else if (ap == 0 && (ad == 242 || ad == 234))   // Battery status
23 andreas 2128
    {
38 andreas 2129
        if (gPageManager)
2130
            gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), mHandle);
23 andreas 2131
    }
36 andreas 2132
    else if (lp == 0 && lv == 81)
2133
    {
2134
        if (gPageManager)
2135
            gPageManager->regCallbackNetState(bind(&TButton::funcNetworkState, this, std::placeholders::_1), mHandle);
2136
    }
51 andreas 2137
    else if (ap == 0 && ad == 122)      // IP Address of server
2138
        sr[0].te = sr[1].te = TConfig::getController();
2139
    else if (ap == 0 && ad == 123)      // Channel number of panel
2140
        sr[0].te = sr[1].te = std::to_string(TConfig::getChannel());
2141
    else if (ap == 0 && ad == 124)      // Network port number of the controller
2142
        sr[0].te = sr[1].te = std::to_string(TConfig::getPort());
2143
    else if (ap == 0 && ad == 199)      // Technical name of panel
2144
        sr[0].te = sr[1].te = TConfig::getPanelType();
2145
    else if (ap == 0 && ad == 1101)     // Path and name of the logfile
2146
        sr[0].te = sr[1].te = TConfig::getLogFile();
15 andreas 2147
}
2148
 
2149
void TButton::addPushFunction(string& func, string& page)
2150
{
2151
    DECL_TRACER("TButton::addPushFunction(string& func, string& page)");
2152
 
2153
    vector<string> allFunc = { "Stan", "Prev", "Show", "Hide", "Togg", "ClearG", "ClearP", "ClearA" };
2154
    vector<string>::iterator iter;
2155
 
2156
    for (iter = allFunc.begin(); iter != allFunc.end(); ++iter)
2157
    {
2158
        if (iter->compare(func) == 0)
2159
        {
2160
            bool found = false;
2161
            vector<PUSH_FUNC_T>::iterator iterPf;
2162
 
118 andreas 2163
            for (iterPf = pushFunc.begin(); iterPf != pushFunc.end(); ++iterPf)
15 andreas 2164
            {
2165
                if (iterPf->pfType.compare(func) == 0)
2166
                {
2167
                    iterPf->pfName = page;
2168
                    found = true;
2169
                    break;
2170
                }
2171
            }
2172
 
2173
            if (!found)
2174
            {
2175
                PUSH_FUNC_T pf;
2176
                pf.pfType = func;
2177
                pf.pfName = page;
2178
                pushFunc.push_back(pf);
2179
            }
108 andreas 2180
 
2181
            break;
15 andreas 2182
        }
2183
    }
2184
}
2185
 
16 andreas 2186
void TButton::clearPushFunction(const string& action)
2187
{
2188
    DECL_TRACER("TButton::clearPushFunction(const string& action)");
2189
 
2190
    if (pushFunc.empty())
2191
        return;
2192
 
2193
    vector<PUSH_FUNC_T>::iterator iter;
2194
 
118 andreas 2195
    for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
16 andreas 2196
    {
2197
        if (iter->pfName.compare(action) == 0)
2198
        {
2199
            pushFunc.erase(iter);
2200
            return;
2201
        }
2202
    }
2203
}
2204
 
8 andreas 2205
void TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)
4 andreas 2206
{
8 andreas 2207
    DECL_TRACER("TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)");
4 andreas 2208
 
8 andreas 2209
    if (!order)
2210
        return;
2211
 
2212
    if (sdo.empty() || sdo.length() != 10)
4 andreas 2213
    {
8 andreas 2214
        *order     = ORD_ELEM_FILL;
2215
        *(order+1) = ORD_ELEM_BITMAP;
2216
        *(order+2) = ORD_ELEM_ICON;
2217
        *(order+3) = ORD_ELEM_TEXT;
2218
        *(order+4) = ORD_ELEM_BORDER;
2219
        return;
2220
    }
2221
 
2222
    int elems = sdo.length() / 2;
2223
 
2224
    for (int i = 0; i < elems; i++)
2225
    {
2226
        int e = atoi(sdo.substr(i * 2, 2).c_str());
2227
 
2228
        if (e < 1 || e > 5)
2229
        {
2230
            MSG_ERROR("Invalid draw order \"" << sdo << "\"!");
2231
            TError::setError();
2232
            return;
2233
        }
2234
 
2235
        *(order+i) = (DRAW_ORDER)e;
2236
    }
2237
}
2238
 
2239
bool TButton::buttonFill(SkBitmap* bm, int instance)
2240
{
2241
    DECL_TRACER("TButton::buttonFill(SkBitmap* bm, int instance)");
2242
 
2243
    if (!bm)
2244
    {
2245
        MSG_ERROR("Invalid bitmap!");
4 andreas 2246
        return false;
2247
    }
2248
 
8 andreas 2249
    SkColor color = TColor::getSkiaColor(sr[instance].cf);
2250
    bm->eraseColor(color);
2251
    return true;
2252
}
2253
 
46 andreas 2254
bool TButton::buttonBitmap(SkBitmap* bm, int inst)
8 andreas 2255
{
2256
    DECL_TRACER("TButton::buttonBitmap(SkBitmap* bm, int instane)");
2257
 
33 andreas 2258
    if (prg_stopped)
2259
        return false;
35 andreas 2260
 
46 andreas 2261
    int instance = inst;
2262
 
2263
    if (inst < 0)
2264
        instance = 0;
2265
    else if ((size_t)inst >= sr.size())
2266
        instance = sr.size() - 1;
2267
 
29 andreas 2268
    if (!sr[instance].mi.empty() && sr[instance].bs.empty())       // Chameleon image?
4 andreas 2269
    {
69 andreas 2270
        MSG_TRACE("Chameleon image consisting of mask " << sr[instance].mi << " and bitmap " << (sr[instance].bm.empty() ? "NONE" : sr[instance].bm) << " ...");
4 andreas 2271
        map<int, IMAGE_t>::iterator iterImages = mImages.find(sr[instance].number);
69 andreas 2272
 
2273
        if (iterImages == mImages.end())
2274
        {
2275
            MSG_ERROR("Missing images " << sr[instance].mi << " and " <<  sr[instance].bm << "!");
2276
            TError::setError();
2277
            return false;
2278
        }
2279
 
6 andreas 2280
        SkBitmap imgRed(iterImages->second.imageMi);
24 andreas 2281
        SkBitmap imgMask;
4 andreas 2282
 
24 andreas 2283
        if (!sr[instance].bm.empty())
2284
            imgMask.installPixels(iterImages->second.imageBm.pixmap());
2285
 
20 andreas 2286
        SkBitmap img = drawImageButton(imgRed, imgMask, sr[instance].mi_width, sr[instance].mi_height, TColor::getSkiaColor(sr[instance].cf), TColor::getSkiaColor(sr[instance].cb));
2287
 
2288
        if (img.empty())
4 andreas 2289
        {
20 andreas 2290
            MSG_ERROR("Error creating the cameleon image \"" << sr[instance].mi << "\" / \"" << sr[instance].bm << "\"!");
2291
            TError::setError();
2292
            return false;
2293
        }
4 andreas 2294
 
20 andreas 2295
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2296
        SkImageInfo info = img.info();
2297
        SkPaint paint;
24 andreas 2298
        paint.setBlendMode(SkBlendMode::kSrcOver);
20 andreas 2299
        ctx.drawBitmap(imgMask, 0, 0, &paint);
4 andreas 2300
 
20 andreas 2301
        POSITION_t position = calcImagePosition(sr[instance].mi_width, sr[instance].mi_height, SC_BITMAP, instance);
7 andreas 2302
 
20 andreas 2303
        if (!position.valid)
2304
        {
2305
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
2306
            TError::setError();
2307
            return false;
4 andreas 2308
        }
10 andreas 2309
 
20 andreas 2310
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2311
        paint.setBlendMode(SkBlendMode::kSrc);
21 andreas 2312
 
2313
        if (sr[instance].sb == 0)
2314
            can.drawBitmap(img, position.left, position.top, &paint);
2315
        else    // Scale to fit
2316
        {
2317
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
2318
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(img);
2319
            can.drawImageRect(im, rect, &paint);
2320
        }
4 andreas 2321
    }
2322
    else if (!sr[instance].bm.empty())
2323
    {
69 andreas 2324
        MSG_TRACE("Drawing normal image " << sr[instance].bm << " ...");
4 andreas 2325
        map<int, IMAGE_t>::iterator iterImages = mImages.find(sr[instance].number);
69 andreas 2326
 
2327
        if (iterImages == mImages.end())
2328
        {
2329
            MSG_ERROR("Missing image " << sr[instance].bm << "!");
97 andreas 2330
            return true;        // We want the button even without an image
69 andreas 2331
        }
2332
 
6 andreas 2333
        SkBitmap image = iterImages->second.imageBm;
4 andreas 2334
 
6 andreas 2335
        if (image.empty())
4 andreas 2336
        {
2337
            MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
2338
            TError::setError();
2339
            return false;
2340
        }
2341
 
99 andreas 2342
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
2343
        POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
2344
//        POSITION_t position = calcImagePosition(sr[instance].bm_width, sr[instance].bm_height, SC_BITMAP, instance);
4 andreas 2345
 
2346
        if (!position.valid)
2347
        {
2348
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
2349
            TError::setError();
2350
            return false;
2351
        }
2352
 
10 andreas 2353
        MSG_DEBUG("Putting bitmap on top of image ...");
2354
        SkPaint paint;
69 andreas 2355
        paint.setBlendMode(SkBlendMode::kSrcOver);
2356
        SkCanvas can(*bm, SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kUnknown_SkPixelGeometry));
21 andreas 2357
 
69 andreas 2358
        if (sr[instance].sb == 0)   // Scale bitmap?
2359
        {                           // No, keep size
46 andreas 2360
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
2361
                can.drawBitmap(image, position.left, position.top, &paint);
49 andreas 2362
            else    // We need only a subset of the image
46 andreas 2363
            {
2364
                MSG_DEBUG("Create a subset of an image ...");
2365
 
2366
                // Create a new Info to have the size of the subset.
2367
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
2368
                size_t byteSize = info.computeMinByteSize();
2369
 
2370
                if (byteSize == 0)
2371
                {
2372
                    MSG_ERROR("Unable to calculate size of image!");
2373
                    TError::setError();
2374
                    return false;
2375
                }
2376
 
2377
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
2378
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
2379
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
2380
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
2381
                can.drawBitmap(part, 0, 0, &paint); // Draw the image
2382
            }
2383
        }
21 andreas 2384
        else    // Scale to fit
2385
        {
99 andreas 2386
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
21 andreas 2387
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
2388
            can.drawImageRect(im, rect, &paint);
2389
        }
6 andreas 2390
    }
8 andreas 2391
    else
6 andreas 2392
    {
8 andreas 2393
        MSG_DEBUG("No bitmap defined.");
2394
    }
4 andreas 2395
 
8 andreas 2396
    return true;
2397
}
4 andreas 2398
 
97 andreas 2399
bool TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)
21 andreas 2400
{
97 andreas 2401
    DECL_TRACER("TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)");
21 andreas 2402
 
33 andreas 2403
    if (prg_stopped)
2404
        return false;
35 andreas 2405
 
21 andreas 2406
    if (!gPrjResources)
2407
    {
2408
        MSG_ERROR("Internal error: Global resource class not initialized!");
2409
        return false;
2410
    }
2411
 
2412
    if (!sr[instance].dynamic)
2413
    {
2414
        MSG_WARNING("Button " << bi << ": \"" << na << "\" is not for remote image!");
2415
        return false;
2416
    }
2417
 
98 andreas 2418
    if (!visible)
99 andreas 2419
    {
2420
        MSG_DEBUG("Dynamic button " << TObject::handleToString(mHandle) << " is invisible. Will not draw it.");
98 andreas 2421
        return true;
99 andreas 2422
    }
98 andreas 2423
 
99 andreas 2424
    MSG_DEBUG("Dynamic button " << TObject::handleToString(mHandle) << " will be drawn ...");
97 andreas 2425
    size_t idx = 0;
21 andreas 2426
 
97 andreas 2427
    if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
2428
    {
2429
        MSG_ERROR("There exists no image resource!");
2430
        return false;
2431
    }
2432
 
2433
    RESOURCE_T resource = gPrjResources->findResource(idx, sr[instance].bm);
2434
 
21 andreas 2435
    if (resource.protocol.empty())
2436
    {
100 andreas 2437
        MSG_WARNING("Resource " << sr[instance].bm << " not found!");
2438
        return true;
21 andreas 2439
    }
2440
 
2441
    string path = resource.path;
2442
 
2443
    if (!resource.file.empty())
2444
        path += "/" + resource.file;
2445
 
94 andreas 2446
    string url = THTTPClient::makeURLs(toLower(resource.protocol), resource.host, 0, path);
21 andreas 2447
 
97 andreas 2448
    if (url.empty())
2449
    {
100 andreas 2450
        MSG_DEBUG("No URL, no bitmap!");
97 andreas 2451
        return true;    // We have no image but the button still exists
2452
    }
2453
 
2454
    SkBitmap image;
2455
 
2456
    if (TImgCache::getBitmap(url, &image))
2457
    {
100 andreas 2458
        MSG_DEBUG("Found image \"" << url << "\" in the cache. Will reuse it.");
99 andreas 2459
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
2460
        POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
97 andreas 2461
 
2462
        if (!position.valid)
2463
        {
2464
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
2465
            TError::setError();
2466
            return false;
2467
        }
2468
 
2469
        SkPaint paint;
2470
        paint.setBlendMode(SkBlendMode::kSrcOver);
2471
        SkCanvas can(*bm, SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kUnknown_SkPixelGeometry));
2472
 
2473
        if (sr[instance].sb == 0)   // Scale bitmap?
2474
        {                           // No, keep size
2475
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
2476
                can.drawBitmap(image, position.left, position.top, &paint);
2477
            else    // We need only a subset of the image
2478
            {
2479
                MSG_DEBUG("Create a subset of an image ...");
2480
 
2481
                // Create a new Info to have the size of the subset.
2482
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
2483
                size_t byteSize = info.computeMinByteSize();
2484
 
2485
                if (byteSize == 0)
2486
                {
2487
                    MSG_ERROR("Unable to calculate size of image!");
2488
                    TError::setError();
2489
                    return false;
2490
                }
2491
 
2492
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
2493
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
2494
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
2495
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
2496
                can.drawBitmap(part, 0, 0, &paint); // Draw the image
2497
            }
2498
        }
2499
        else    // Scale to fit
2500
        {
99 andreas 2501
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
97 andreas 2502
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
2503
            can.drawImageRect(im, rect, &paint);
2504
        }
2505
 
2506
        return true;
2507
    }
2508
 
21 andreas 2509
    try
2510
    {
94 andreas 2511
        // First me must add the credential for the image into a bitmap cache element
2512
        BITMAP_CACHE bc;
2513
        bc.top = tp;
2514
        bc.left = lt;
2515
        bc.width = wt;
2516
        bc.height = ht;
2517
        bc.bi = bi;
97 andreas 2518
        bc.show = show;
94 andreas 2519
        bc.handle = getHandle();
2520
        bc.parent = getParent();
2521
        bc.bitmap = *bm;
2522
        addToBitmapCache(bc);
97 andreas 2523
 
2524
        if (state)
2525
            *state = true;  // Prevent the calling method from displaying the button
2526
 
21 andreas 2527
        MSG_TRACE("Starting thread for loading a dynamic image ...");
94 andreas 2528
        mThrRes = std::thread([=] { this->funcResource(&resource, url, bc, instance); });
21 andreas 2529
        MSG_TRACE("Thread started. Detaching ...");
2530
        mThrRes.detach();
2531
        MSG_TRACE("Thread is running and detached.");
2532
    }
2533
    catch (std::exception& e)
2534
    {
28 andreas 2535
        MSG_ERROR("Error starting the resource thread: " << e.what());
21 andreas 2536
    }
2537
 
2538
    return true;
2539
}
2540
 
99 andreas 2541
/*
2542
 * Draws the elements of a button starting at the point where the bitmap was
2543
 * already drawed. Everything coming afterwards acording to the draw order
2544
 * is drawed in the desired order.
2545
 * This method is called out of a thread to draw a button with an external
2546
 * image coming from a WEB server.
2547
 */
2548
bool TButton::drawAlongOrder(SkBitmap *imgButton, int instance)
2549
{
2550
    DECL_TRACER("TButton::drawAlongOrder(SkBitmap *imgButton, int instance)");
2551
 
2552
    bool cont = false;
2553
 
2554
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
2555
    {
2556
        if (!cont && mDOrder[i] == ORD_ELEM_BITMAP)
2557
        {
2558
            cont = true;
2559
            continue;
2560
        }
100 andreas 2561
        else if (!cont)
2562
            continue;
99 andreas 2563
 
2564
        if (mDOrder[i] == ORD_ELEM_FILL)
2565
        {
2566
            if (!buttonFill(imgButton, instance))
2567
            {
2568
                mutex_button.unlock();
2569
                return false;
2570
            }
2571
        }
2572
        else if (mDOrder[i] == ORD_ELEM_ICON)
2573
        {
2574
            if (!buttonIcon(imgButton, instance))
2575
            {
2576
                mutex_button.unlock();
2577
                return false;
2578
            }
2579
        }
2580
        else if (mDOrder[i] == ORD_ELEM_TEXT)
2581
        {
2582
            if (!buttonText(imgButton, instance))
2583
            {
2584
                mutex_button.unlock();
2585
                return false;
2586
            }
2587
        }
2588
        else if (mDOrder[i] == ORD_ELEM_BORDER)
2589
        {
2590
            if (!buttonBorder(imgButton, instance))
2591
            {
2592
                mutex_button.unlock();
2593
                return false;
2594
            }
2595
        }
2596
    }
2597
 
2598
    return true;
2599
}
2600
 
94 andreas 2601
void TButton::funcResource(const RESOURCE_T* resource, const std::string& url, BITMAP_CACHE bc, int instance)
21 andreas 2602
{
2603
    DECL_TRACER("TButton::funcResource(RESOURCE_T* resource, std::string& url, SkBitmap* bm, int instance)");
2604
 
97 andreas 2605
    if (prg_stopped || killed || _restart_ || !resource)
35 andreas 2606
        return;
2607
 
21 andreas 2608
    if (resource->refresh > 0 && !resource->dynamo)      // Periodically refreshing image?
2609
    {
2610
        MSG_DEBUG("Retrieving periodicaly refreshed image");
2611
 
97 andreas 2612
        if (!bc.handle || !bc.parent || bc.bi <= 1)
21 andreas 2613
        {
2614
            MSG_ERROR("Invalid button. Can't make a dynamo image!");
2615
            return;
2616
        }
2617
 
97 andreas 2618
        THR_REFRESH_t *thref = _findResource(bc.handle, bc.parent, bc.bi);
21 andreas 2619
        TImageRefresh *mImageRefresh = nullptr;
2620
 
2621
        if (!thref)
2622
        {
2623
            MSG_DEBUG("Creating a new refresh thread");
2624
            mImageRefresh = new TImageRefresh();
2625
            mImageRefresh->registerCallback(bind(&TButton::_imageRefresh, this, std::placeholders::_1));
2626
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
2627
            mImageRefresh->setUsername(resource->user);
2628
            mImageRefresh->setPassword(resource->password);
2629
 
2630
            if (resource->preserve)
2631
                mImageRefresh->setRunOnce();
2632
 
97 andreas 2633
            _addResource(mImageRefresh, bc.handle, bc.parent, bc.bi);
21 andreas 2634
        }
2635
        else
2636
        {
2637
            mImageRefresh = thref->mImageRefresh;
2638
 
2639
            if (!mImageRefresh)
2640
            {
2641
                MSG_ERROR("Error creating a new refresh class!");
2642
                return;
2643
            }
2644
 
2645
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
2646
            mImageRefresh->setUsername(resource->user);
2647
            mImageRefresh->setPassword(resource->password);
2648
 
2649
            if (resource->preserve)
2650
                mImageRefresh->setRunOnce();
2651
        }
2652
 
97 andreas 2653
        if (mImageRefresh->isRunning())
2654
            mImageRefresh->stopWait();
2655
 
2656
        if (!mImageRefresh->isRunning() && !_restart_)
21 andreas 2657
        {
2658
            MSG_DEBUG("Starting a refresh thread.");
2659
            mImageRefresh->run(url);
2660
        }
2661
    }
2662
    else if (resource->refresh == 0 && !resource->dynamo)
2663
    {
2664
        MSG_DEBUG("Retrieving single image");
2665
 
94 andreas 2666
        if (bc.handle == 0)
2667
        {
2668
            MSG_ERROR("Invalid bitmap cache!");
2669
            return;
2670
        }
2671
 
97 andreas 2672
        // Check whether we have this image already
2673
        SkBitmap bitm;
2674
        bool cached = false;
2675
 
2676
        cached = TImgCache::getBitmap(url, &bitm);
94 andreas 2677
        BITMAP_CACHE bmCache = getBCentryByHandle(bc.handle, bc.parent);
2678
 
97 andreas 2679
        if (!cached)    // If the bitmap was not in cache we must load it
94 andreas 2680
        {
97 andreas 2681
            MSG_DEBUG("Image not in cache. Downloading it ...");
2682
            THTTPClient *WEBClient = nullptr;
94 andreas 2683
 
97 andreas 2684
            if (bmCache.handle == 0)
2685
            {
2686
                MSG_ERROR("Couldn't find the handle " << TObject::handleToString(bc.handle) << " in bitmap cache!");
2687
                return;
2688
            }
2689
 
21 andreas 2690
            char *content = nullptr;
2691
            size_t length = 0, contentlen = 0;
94 andreas 2692
            WEBClient = new THTTPClient;
21 andreas 2693
 
94 andreas 2694
            if (!WEBClient || (content = WEBClient->tcall(&length, url, resource->user, resource->password)) == nullptr)
97 andreas 2695
            {
2696
                if (WEBClient)
2697
                    delete WEBClient;
2698
 
2699
                if (bc.show)
2700
                {
2701
                    setReady(bmCache.handle);
2702
                    showBitmapCache();
2703
                }
2704
                else
2705
                    setInvalid(bc.handle);
2706
 
21 andreas 2707
                return;
97 andreas 2708
            }
21 andreas 2709
 
94 andreas 2710
            contentlen = WEBClient->getContentSize();
21 andreas 2711
            MSG_DEBUG("Loaded " << contentlen << " bytes:");
2712
            sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
2713
 
97 andreas 2714
            if (!data || _restart_)
21 andreas 2715
            {
94 andreas 2716
                delete WEBClient;
21 andreas 2717
                MSG_ERROR("Error making image data!");
97 andreas 2718
 
2719
                if (bc.show)
2720
                {
2721
                    setReady(bmCache.handle);
2722
                    showBitmapCache();
2723
                }
2724
                else
2725
                    setInvalid(bc.handle);
2726
 
21 andreas 2727
                return;
2728
            }
2729
 
2730
            SkBitmap image;
2731
 
2732
            if (!DecodeDataToBitmap(data, &image))
2733
            {
97 andreas 2734
                delete WEBClient;
21 andreas 2735
                MSG_ERROR("Error creating an image!");
97 andreas 2736
 
2737
                if (bc.show)
2738
                {
2739
                    setReady(bmCache.handle);
2740
                    showBitmapCache();
2741
                }
2742
                else
2743
                    setInvalid(bc.handle);
2744
 
21 andreas 2745
                return;
2746
            }
2747
 
97 andreas 2748
            // Put this image into the static image cache
2749
            TImgCache::addImage(url, image);
2750
            // Make the button complete
94 andreas 2751
            loadImage(&bmCache.bitmap, image, instance);
99 andreas 2752
            drawAlongOrder(&bmCache.bitmap, instance);
94 andreas 2753
            setBCBitmap(bmCache.handle, bmCache.bitmap);
2754
            setReady(bmCache.handle);
2755
            delete WEBClient;
97 andreas 2756
            // Display the image
94 andreas 2757
            showBitmapCache();
21 andreas 2758
            return;
2759
        }
97 andreas 2760
        else
21 andreas 2761
        {
97 andreas 2762
            MSG_DEBUG("Found image in cache. Using it ...");
2763
            loadImage(&bmCache.bitmap, bitm, instance);
2764
            setInvalid(bc.handle);
94 andreas 2765
 
97 andreas 2766
            if (bc.show && _displayButton)
2767
                _displayButton(bc.handle, bc.parent, (unsigned char *)bmCache.bitmap.getPixels(), bc.width, bc.height, bmCache.bitmap.info().minRowBytes(), bc.left, bc.top);
21 andreas 2768
        }
2769
    }
97 andreas 2770
    else if (!_restart_)
21 andreas 2771
    {
2772
        MSG_DEBUG("Retrieving a video");
2773
 
2774
        if (_playVideo && !prg_stopped)
2775
        {
2776
            ulong parent = (mHandle >> 16) & 0x0000ffff;
2777
            _playVideo(mHandle, parent, lt, tp, wt, ht, url, resource->user, resource->password);
2778
        }
2779
    }
2780
}
2781
 
38 andreas 2782
void TButton::funcBattery(int level, bool charging, int /* chargeType */)
23 andreas 2783
{
38 andreas 2784
    DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
59 andreas 2785
 
23 andreas 2786
    // Battery level is always a bargraph
59 andreas 2787
    if (ap == 0 && ad == 242)       // Not charging
23 andreas 2788
    {
38 andreas 2789
        mEnabled = !charging;
23 andreas 2790
 
2791
        if (!mEnabled && visible)
2792
            hide(true);
2793
        else if (mEnabled)
2794
        {
2795
            visible = true;
38 andreas 2796
            drawBargraph(mActInstance, level, visible);
23 andreas 2797
        }
2798
    }
59 andreas 2799
    else if (ap == 0 && ad == 234)  // Charging
23 andreas 2800
    {
38 andreas 2801
        mEnabled = charging;
23 andreas 2802
 
2803
        if (!mEnabled && visible)
2804
            hide(true);
2805
        else if (mEnabled)
2806
        {
2807
            visible = true;
38 andreas 2808
            drawBargraph(mActInstance, level, visible);
23 andreas 2809
        }
2810
    }
2811
}
2812
 
36 andreas 2813
void TButton::funcNetworkState(int level)
2814
{
2815
    DECL_TRACER("TButton::funcNetworkState(int level)");
2816
 
38 andreas 2817
    if (level >= rl && level <= rh)
2818
    {
2819
        mLastLevel = level;
2820
        drawMultistateBargraph(mLastLevel);
2821
    }
36 andreas 2822
}
2823
 
21 andreas 2824
bool TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)
2825
{
2826
    DECL_TRACER("TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)");
2827
 
94 andreas 2828
    if (!bm)
2829
    {
2830
        MSG_WARNING("Got no image to load!");
2831
        return false;
2832
    }
2833
 
21 andreas 2834
    SkImageInfo info = image.info();
99 andreas 2835
    IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
2836
    POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : info.width()), (sr[instance].sb ? isize.height : info.height()), SC_BITMAP, instance);
2837
//    POSITION_t position = calcImagePosition(info.width(), info.height(), SC_BITMAP, instance);
21 andreas 2838
 
2839
    if (!position.valid)
2840
    {
2841
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
2842
        return false;
2843
    }
2844
 
94 andreas 2845
    MSG_DEBUG("New image position: left=" << position.left << ", top=" << position.top << ", width=" << position.width << ", height=" << position.height);
2846
    MSG_DEBUG("Image size : width=" << info.width() << ", height=" << info.height());
2847
    MSG_DEBUG("Bitmap size: width=" << bm->info().width() << ", height=" << bm->info().height());
21 andreas 2848
    MSG_DEBUG("Putting bitmap on top of image ...");
2849
    SkPaint paint;
2850
    paint.setBlendMode(SkBlendMode::kSrc);
2851
 
2852
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2853
 
2854
    if (sr[instance].sb == 0)
2855
        can.drawBitmap(image, position.left, position.top, &paint);
2856
    else    // Scale to fit
2857
    {
94 andreas 2858
        paint.setFilterQuality(kHigh_SkFilterQuality);
99 andreas 2859
        SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
21 andreas 2860
        sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
2861
        can.drawImageRect(im, rect, &paint);
2862
    }
2863
 
2864
    return true;
2865
}
2866
 
111 andreas 2867
bool TButton::barLevel(SkBitmap* bm, int, int level)
15 andreas 2868
{
100 andreas 2869
    DECL_TRACER("TButton::barLevel(SkBitmap* bm, int inst, int level)");
15 andreas 2870
 
30 andreas 2871
    if (!sr[0].mi.empty()  && sr[0].bs.empty() && !sr[1].bm.empty())       // Chameleon image?
15 andreas 2872
    {
38 andreas 2873
        MSG_TRACE("Chameleon image ...");
15 andreas 2874
        map<int, IMAGE_t>::iterator iterImages1 = mImages.find(sr[0].number);
2875
        map<int, IMAGE_t>::iterator iterImages2 = mImages.find(sr[1].number);
2876
        SkBitmap imgRed(iterImages1->second.imageMi);
2877
        SkBitmap imgMask(iterImages2->second.imageBm);
2878
 
20 andreas 2879
        SkBitmap img;
2880
        SkPixmap pixmapRed = imgRed.pixmap();
2881
        SkPixmap pixmapMask;
15 andreas 2882
 
20 andreas 2883
        if (!imgMask.empty())
2884
            pixmapMask = imgMask.pixmap();
15 andreas 2885
 
20 andreas 2886
        int width = sr[0].mi_width;
2887
        int height = sr[0].mi_height;
2888
        int startX = 0;
2889
        int startY = 0;
100 andreas 2890
        // Calculation: width / <effective pixels> * level
2891
        // Calculation: height / <effective pixels> * level
20 andreas 2892
        if (dr.compare("horizontal") == 0)
2893
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
2894
        else
2895
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
2896
 
59 andreas 2897
        if (ri && dr.compare("horizontal") == 0)
20 andreas 2898
        {
59 andreas 2899
            startX = sr[0].mi_width - width;
20 andreas 2900
            width = sr[0].mi_width;
59 andreas 2901
        }
2902
        else if (dr.compare("horizontal") != 0)
2903
        {
2904
            startY = sr[0].mi_height - height;
20 andreas 2905
            height = sr[0].mi_height;
2906
        }
15 andreas 2907
 
20 andreas 2908
        img.allocPixels(SkImageInfo::MakeN32Premul(sr[0].mi_width, sr[0].mi_height));
2909
        SkCanvas canvas(img);
2910
        SkColor col1 = TColor::getSkiaColor(sr[1].cf);
2911
        SkColor col2 = TColor::getSkiaColor(sr[1].cb);
15 andreas 2912
 
20 andreas 2913
        for (int ix = 0; ix < sr[0].mi_width; ix++)
2914
        {
2915
            for (int iy = 0; iy < sr[0].mi_height; iy++)
2916
            {
2917
                SkPaint paint;
2918
                SkColor pixel;
15 andreas 2919
 
20 andreas 2920
                if (ix >= startX && ix < width && iy >= startY && iy < height)
15 andreas 2921
                {
20 andreas 2922
                    SkColor pixelRed = pixmapRed.getColor(ix, iy);
2923
                    SkColor pixelMask;
15 andreas 2924
 
20 andreas 2925
                    if (!imgMask.empty())
2926
                        pixelMask = pixmapMask.getColor(ix, iy);
15 andreas 2927
                    else
20 andreas 2928
                        pixelMask = SK_ColorWHITE;
15 andreas 2929
 
20 andreas 2930
                    pixel = baseColor(pixelRed, pixelMask, col1, col2);
15 andreas 2931
                }
20 andreas 2932
                else
2933
                    pixel = SK_ColorTRANSPARENT;
15 andreas 2934
 
20 andreas 2935
                paint.setColor(pixel);
2936
                canvas.drawPoint(ix, iy, paint);
15 andreas 2937
            }
20 andreas 2938
        }
15 andreas 2939
 
20 andreas 2940
        if (img.empty())
2941
        {
2942
            MSG_ERROR("Error creating the cameleon image \"" << sr[0].mi << "\" / \"" << sr[0].bm << "\"!");
2943
            TError::setError();
2944
            return false;
2945
        }
15 andreas 2946
 
20 andreas 2947
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2948
        SkPaint paint;
2949
        paint.setBlendMode(SkBlendMode::kSrcATop);
2950
        ctx.drawBitmap(imgMask, 0, 0, &paint);
15 andreas 2951
 
20 andreas 2952
        POSITION_t position = calcImagePosition(sr[0].mi_width, sr[0].mi_height, SC_BITMAP, 0);
15 andreas 2953
 
20 andreas 2954
        if (!position.valid)
2955
        {
2956
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
2957
            TError::setError();
2958
            return false;
15 andreas 2959
        }
2960
 
20 andreas 2961
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
2962
        paint.setBlendMode(SkBlendMode::kSrc);
2963
        can.drawBitmap(img, position.left, position.top, &paint);
15 andreas 2964
    }
59 andreas 2965
    else if (!sr[0].bm.empty() && !sr[1].bm.empty())
15 andreas 2966
    {
23 andreas 2967
        MSG_TRACE("Drawing normal image ...");
59 andreas 2968
        map<int, IMAGE_t>::iterator iterImages1 = mImages.find(sr[0].number);   // State when level = 0%
2969
        map<int, IMAGE_t>::iterator iterImages2 = mImages.find(sr[1].number);   // State when level = 100%
2970
        SkBitmap image1 = iterImages1->second.imageBm;  // 0%
2971
        SkBitmap image2 = iterImages2->second.imageBm;  // 100%
2972
        SkCanvas can_bm(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
15 andreas 2973
 
59 andreas 2974
 
23 andreas 2975
        if (image1.empty())
15 andreas 2976
        {
23 andreas 2977
            MSG_ERROR("Error creating the image \"" << sr[0].bm << "\"!");
15 andreas 2978
            TError::setError();
2979
            return false;
2980
        }
2981
 
23 andreas 2982
        if (image2.empty())
2983
        {
2984
            MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
2985
            TError::setError();
2986
            return false;
2987
        }
2988
 
2989
        int width = sr[1].bm_width;
2990
        int height = sr[1].bm_height;
15 andreas 2991
        int startX = 0;
2992
        int startY = 0;
2993
 
100 andreas 2994
        // Calculation: width / <effective pixels> * level
2995
        // Calculation: height / <effective pixels> * level
15 andreas 2996
        if (dr.compare("horizontal") == 0)
2997
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
2998
        else
2999
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
3000
 
59 andreas 3001
        if (ri && dr.compare("horizontal") == 0)     // range inverted?
15 andreas 3002
        {
59 andreas 3003
            startX = sr[0].mi_width - width;
15 andreas 3004
            width = sr[0].mi_width;
59 andreas 3005
        }
3006
        else if (dr.compare("horizontal") != 0)
3007
        {
3008
            startY = sr[0].mi_height - height;
15 andreas 3009
            height = sr[0].mi_height;
3010
        }
3011
 
28 andreas 3012
        MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
23 andreas 3013
        MSG_TRACE("Creating bargraph ...");
59 andreas 3014
        SkBitmap img_bar;
3015
        img_bar.allocPixels(SkImageInfo::MakeN32Premul(sr[1].bm_width, sr[1].bm_height));
3016
        img_bar.eraseColor(SK_ColorTRANSPARENT);
3017
        SkCanvas bar(img_bar, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
23 andreas 3018
 
3019
        for (int ix = 0; ix < sr[1].bm_width; ix++)
3020
        {
3021
            for (int iy = 0; iy < sr[1].bm_height; iy++)
3022
            {
3023
                SkPaint paint;
3024
                SkColor pixel;
3025
 
3026
                if (ix >= startX && ix < width && iy >= startY && iy < height)
3027
                    pixel = image2.getColor(ix, iy);
3028
                else
3029
                    pixel = SK_ColorTRANSPARENT;
3030
 
3031
                paint.setColor(pixel);
59 andreas 3032
                bar.drawPoint(ix, iy, paint);
23 andreas 3033
            }
3034
        }
3035
 
15 andreas 3036
        SkPaint paint;
59 andreas 3037
        paint.setBlendMode(SkBlendMode::kSrc);
3038
        can_bm.drawBitmap(image1, 0, 0, &paint);
23 andreas 3039
        paint.setBlendMode(SkBlendMode::kSrcATop);
59 andreas 3040
        can_bm.drawBitmap(img_bar, 0, 0, &paint);      // Draw the above created image over the 0% image
15 andreas 3041
    }
3042
    else
3043
    {
38 andreas 3044
        MSG_TRACE("No bitmap defined.");
3045
        int width = wt;
3046
        int height = ht;
15 andreas 3047
        int startX = 0;
3048
        int startY = 0;
3049
 
100 andreas 3050
        // Calculation: width / <effective pixels> * level = <level position>
3051
        // Calculation: height / <effective pixels> * level = <level position>
15 andreas 3052
        if (dr.compare("horizontal") == 0)
3053
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
3054
        else
3055
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
3056
 
59 andreas 3057
        if (ri && dr.compare("horizontal") == 0)
15 andreas 3058
        {
59 andreas 3059
            startX = wt - width;
38 andreas 3060
            width = wt;
59 andreas 3061
        }
3062
        else if (dr.compare("horizontal") != 0)
3063
        {
3064
            startY = ht - height;
38 andreas 3065
            height = ht;
15 andreas 3066
        }
3067
 
3068
        SkPaint paint;
3069
        paint.setBlendMode(SkBlendMode::kSrc);
3070
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
20 andreas 3071
        paint.setStyle(SkPaint::kFill_Style);
3072
        paint.setAntiAlias(true);
3073
        paint.setStrokeWidth(4);
38 andreas 3074
        paint.setColor(TColor::getSkiaColor(sr[1].cf));
3075
        MSG_DEBUG("Drawing rectangle: X=" << startX << ", Y=" << startY << ", W=" << width << ", H=" << height << ", level=" << level);
15 andreas 3076
        SkRect dst;
3077
        dst.setXYWH(startX, startY, width, height);
20 andreas 3078
        can.drawRect(dst, paint);
99 andreas 3079
        // If we have a slider button defined, we must draw it. To do it, we
3080
        // must look into the system resources to find the credentials to draw
3081
        // the button.
3082
        if (!sd.empty())
3083
        {
3084
            MSG_DEBUG("Attempt to draw the slider button \"" << sd << "\".");
100 andreas 3085
            int innerW = 0;
3086
            int innerH = 0;
3087
 
99 andreas 3088
            SkBitmap slButton = drawSliderButton(sd, TColor::getSkiaColor(sc));
3089
 
3090
            if (slButton.empty())
3091
            {
3092
                MSG_ERROR("Error drawing the slicer button " << sd);
3093
                return true;
3094
            }
3095
 
3096
            double scaleW, scaleH;
100 andreas 3097
            int border_size = getBorderSize(sr[0].bs);
99 andreas 3098
 
3099
            if (dr.compare("horizontal") != 0)
3100
            {
3101
                double scale;
100 andreas 3102
                innerH = (int)((double)(height - border_size * 2 - slButton.info().height() / 2) / ((double)rh - (double)rl) * (double)level) + border_size + slButton.info().height() / 2;
3103
                innerW = width;
3104
                scale = (double)(wt - border_size * 2) / (double)slButton.info().width();
99 andreas 3105
                scaleW = scale;
100 andreas 3106
                scaleH = 1.0;
3107
 
3108
                if (!ri)
3109
                    innerH = height - innerH;
99 andreas 3110
            }
3111
            else
3112
            {
3113
                double scale;
100 andreas 3114
                scale = (double)(ht - border_size * 2) / (double)slButton.info().height();
3115
                scaleW = 1.0;
99 andreas 3116
                scaleH = scale;
100 andreas 3117
                innerW = (int)((double)(width - border_size * 2 - slButton.info().width() / 2) / ((double)rh - (double)rl) * (double)level) + border_size + slButton.info().width() / 2;
3118
                innerH = height;
3119
 
3120
                if (!ri)
3121
                    innerW = width - innerW;
99 andreas 3122
            }
100 andreas 3123
 
99 andreas 3124
            if (scaleImage(&slButton, scaleW, scaleH))
3125
            {
3126
                int w = slButton.info().width();
3127
                int h = slButton.info().height();
3128
 
3129
                if (dr.compare("horizontal") == 0)
3130
                {
100 andreas 3131
                    int pos = innerW;
3132
                    dst.setXYWH(pos - w / 2, border_size, w, h);
99 andreas 3133
                }
3134
                else
3135
                {
100 andreas 3136
                    int pos = innerH;
3137
                    dst.setXYWH(border_size, pos - h / 2, w, h);
99 andreas 3138
                }
3139
 
3140
                SkPaint pnt;
100 andreas 3141
                pnt.setBlendMode(SkBlendMode::kSrcOver);
99 andreas 3142
                can.drawBitmapRect(slButton, dst, &pnt);
3143
            }
3144
        }
15 andreas 3145
    }
3146
 
3147
    return true;
3148
}
3149
 
99 andreas 3150
SkBitmap TButton::drawSliderButton(const string& slider, SkColor col)
3151
{
3152
    DECL_TRACER("TButton::drawSliderButton(const string& slider)");
3153
 
3154
    SkBitmap slButton;
3155
    // First we look for the slider button.
3156
    if (!gPageManager || !gPageManager->getSystemDraw()->existSlider(slider))
3157
        return slButton;
3158
 
3159
    // There exists one with the wanted name. We grab it and create
3160
    // the images from the files.
3161
    SLIDER_STYLE_t sst;
3162
 
3163
    if (!gPageManager->getSystemDraw()->getSlider(slider, &sst))    // should never be true!
3164
    {
3165
        MSG_ERROR("No slider entry found!");
3166
        return slButton;
3167
    }
3168
 
3169
    int width, height;
3170
 
3171
    if (dr.compare("horizontal") != 0)
3172
    {
100 andreas 3173
        width = (sst.fixedSize / 2) * 2 + sst.fixedSize;
99 andreas 3174
        height = sst.fixedSize;
3175
    }
3176
    else
3177
    {
3178
        width = sst.fixedSize;
100 andreas 3179
        height = (sst.fixedSize / 2) * 2 + sst.fixedSize;
99 andreas 3180
    }
3181
 
3182
    // Retrieve all available slider graphics files from the system
3183
    vector<SLIDER_t> sltList = gPageManager->getSystemDraw()->getSliderFiles(slider);
3184
 
3185
    if (sltList.empty())
3186
    {
3187
        MSG_ERROR("No system slider graphics found!");
3188
        return SkBitmap();
3189
    }
3190
 
3191
    SkPaint paint;
3192
    paint.setBlendMode(SkBlendMode::kSrc);
3193
    slButton.allocN32Pixels(width, height);
3194
    slButton.eraseColor(SK_ColorTRANSPARENT);
3195
    SkCanvas slCan(slButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3196
    vector<SLIDER_t>::iterator sltIter;
3197
    // Loop through list of slider graphic files
3198
    for (sltIter = sltList.begin(); sltIter != sltList.end(); ++sltIter)
3199
    {
3200
        SkBitmap slPart;
3201
        SkBitmap slPartAlpha;
3202
        SkRect dst;
3203
 
3204
        if (dr.compare("horizontal") != 0 && (sltIter->type == SGR_LEFT || sltIter->type == SGR_RIGHT || sltIter->type == SGR_VERTICAL))    // vertical slider
3205
        {
3206
            if (!retrieveImage(sltIter->path, &slPart))     // Get the mask
3207
            {
3208
                MSG_ERROR("Missing slider button mask image " << sltIter->path);
3209
                return SkBitmap();
3210
            }
3211
 
3212
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))   // Get the alpha mask
3213
            {
3214
                MSG_ERROR("Missing slider button alpha image " << sltIter->pathAlpha);
3215
                return SkBitmap();
3216
            }
3217
 
3218
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
3219
 
3220
            if (sl.empty())
3221
                return sl;
3222
 
3223
            switch (sltIter->type)
3224
            {
3225
                case SGR_LEFT:      dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
3226
 
3227
                case SGR_VERTICAL:
100 andreas 3228
                    stretchImageWidth(&sl, sst.fixedSize);
99 andreas 3229
                    dst.setXYWH(sst.fixedSize / 2, 0, sl.info().width(), sl.info().height());
3230
                break;
3231
 
100 andreas 3232
                case SGR_RIGHT:     dst.setXYWH((sst.fixedSize / 2) + sst.fixedSize, 0, sl.info().width(), sl.info().height()); break;
99 andreas 3233
 
3234
                default:
3235
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
3236
            }
3237
 
3238
            slCan.drawBitmapRect(sl, dst, &paint);
3239
        }
3240
        else if (dr.compare("horizontal") == 0 && (sltIter->type == SGR_TOP || sltIter->type == SGR_BOTTOM || sltIter->type == SGR_HORIZONTAL)) // horizontal slider
3241
        {
3242
            if (!retrieveImage(sltIter->path, &slPart))
3243
            {
3244
                MSG_ERROR("Missing slider button image " << sltIter->path);
3245
                return SkBitmap();
3246
            }
3247
 
3248
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))
3249
            {
3250
                MSG_ERROR("Missing slider button image " << sltIter->pathAlpha);
3251
                return SkBitmap();
3252
            }
3253
 
3254
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
3255
 
3256
            if (sl.empty())
3257
                return sl;
3258
 
3259
            switch (sltIter->type)
3260
            {
3261
                case SGR_TOP:       dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
3262
 
3263
                case SGR_HORIZONTAL:
100 andreas 3264
                    stretchImageHeight(&sl, sst.fixedSize);
99 andreas 3265
                    dst.setXYWH(0, sst.fixedSize / 2, sl.info().width(), sl.info().height());
3266
                break;
3267
 
100 andreas 3268
                case SGR_BOTTOM:    dst.setXYWH(0, (sst.fixedSize / 2) + sst.fixedSize, sl.info().width(), sl.info().height()); break;
99 andreas 3269
 
3270
                default:
3271
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
3272
            }
3273
 
3274
            slCan.drawBitmapRect(sl, dst, &paint);
3275
        }
3276
    }
3277
 
3278
    return slButton;
3279
}
3280
 
8 andreas 3281
bool TButton::buttonIcon(SkBitmap* bm, int instance)
3282
{
3283
    DECL_TRACER("TButton::buttonIcon(SkBitmap* bm, int instance)");
3284
 
3285
    if (sr[instance].ii <= 0)
3286
    {
3287
        MSG_TRACE("No icon defined!");
3288
        return true;
7 andreas 3289
    }
3290
 
8 andreas 3291
    MSG_DEBUG("Drawing an icon ...");
3292
 
3293
    if (!gIcons)
3294
    {
3295
        gIcons = new TIcons();
3296
 
3297
        if (TError::isError())
3298
        {
3299
            MSG_ERROR("Error initializing icons!");
3300
            return false;
3301
        }
3302
    }
3303
 
3304
    string file = gIcons->getFile(sr[instance].ii);
3305
    MSG_DEBUG("Loading icon file " << file);
3306
    sk_sp<SkData> image;
3307
    SkBitmap icon;
3308
 
3309
    if (!(image = readImage(file)))
3310
        return false;
3311
 
3312
    DecodeDataToBitmap(image, &icon);
3313
 
3314
    if (icon.empty())
3315
    {
3316
        MSG_WARNING("Could not create an icon for element " << sr[instance].ii << " on button " << bi << " (" << na << ")");
3317
        return false;
3318
    }
3319
 
3320
    SkImageInfo info = icon.info();
3321
    POSITION_t position = calcImagePosition(icon.width(), icon.height(), SC_ICON, instance);
3322
 
3323
    if (!position.valid)
3324
    {
3325
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
3326
        TError::setError();
3327
        return false;
3328
    }
3329
 
10 andreas 3330
    MSG_DEBUG("Putting Icon on top of bitmap ...");
3331
    SkPaint paint;
3332
    paint.setBlendMode(SkBlendMode::kSrcOver);
3333
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
8 andreas 3334
 
17 andreas 3335
    if (position.overflow)
3336
    {
3337
        SkIRect irect;
3338
        SkRect bdst;
3339
        SkBitmap dst;
3340
        int left = (position.left >= 0) ? 0 : position.left * -1;
3341
        int top = (position.top >= 0) ? 0 : position.top * -1;
3342
        int width = std::min(wt, info.width());
3343
        int height = std::min(ht, info.height());
3344
        irect.setXYWH(left, top, width, height);
3345
        bm->getBounds(&bdst);
3346
        can.drawBitmapRect(icon, irect, bdst, &paint);
3347
    }
3348
    else
3349
        can.drawBitmap(icon, position.left, position.top, &paint);
3350
 
8 andreas 3351
    return true;
3352
}
3353
 
38 andreas 3354
bool TButton::buttonText(SkBitmap* bm, int inst)
8 andreas 3355
{
38 andreas 3356
    DECL_TRACER("TButton::buttonText(SkBitmap* bm, int inst)");
8 andreas 3357
 
38 andreas 3358
    int instance = inst;
3359
 
3360
    if ((size_t)instance >= sr.size())
3361
        instance = sr.size() - 1;
3362
    else if (instance < 0)
3363
        instance = 0;
3364
 
67 andreas 3365
    if (sr[instance].te.empty() || !mFonts)     // Is there a text and fonts?
3366
    {                                           // No, then return
3367
        MSG_DEBUG("Empty text string.");
3368
        return true;
3369
    }
3370
 
3371
    MSG_DEBUG("Searching for font number " << sr[instance].fi << " with text " << sr[instance].te);
3372
    FONT_T font = mFonts->getFont(sr[instance].fi);
3373
 
3374
    if (!font.file.empty())
7 andreas 3375
    {
67 andreas 3376
        SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3377
        sk_sp<SkTypeface> typeFace = mFonts->getTypeFace(sr[instance].fi);
7 andreas 3378
 
67 andreas 3379
        if (!typeFace)
6 andreas 3380
        {
67 andreas 3381
            MSG_ERROR("Error creating type face " << font.fullName);
3382
            TError::setError();
3383
            return false;
3384
        }
7 andreas 3385
 
67 andreas 3386
        SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
3387
        SkFont skFont(typeFace, fontSizePt);
3388
        skFont.setEdging(SkFont::Edging::kAntiAlias);
69 andreas 3389
        MSG_DEBUG("Wanted font size: " << font.size << ", this is " << fontSizePt << " pt");
19 andreas 3390
 
67 andreas 3391
        SkPaint paint;
3392
        paint.setAntiAlias(true);
69 andreas 3393
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
67 andreas 3394
        paint.setStyle(SkPaint::kFill_Style);
8 andreas 3395
 
67 andreas 3396
        SkFontMetrics metrics;
3397
        skFont.getMetrics(&metrics);
3398
        int lines = numberLines(sr[instance].te);
69 andreas 3399
        MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
67 andreas 3400
        MSG_DEBUG("fCapHeight:    " << metrics.fCapHeight);
3401
        MSG_DEBUG("fAscent:       " << metrics.fAscent);
3402
        MSG_DEBUG("fDescent:      " << metrics.fDescent);
3403
        MSG_DEBUG("fLeading:      " << metrics.fLeading);
3404
        MSG_DEBUG("fXHeight:      " << metrics.fXHeight);
69 andreas 3405
 
67 andreas 3406
        MSG_DEBUG("Found " << lines << " lines.");
65 andreas 3407
 
67 andreas 3408
        if (lines > 1 || sr[instance].ww)
3409
        {
3410
            vector<string> textLines;
3411
 
3412
            if (!sr[instance].ww)
69 andreas 3413
                textLines = splitLine(sr[instance].te);
67 andreas 3414
            else
7 andreas 3415
            {
67 andreas 3416
                textLines = splitLine(sr[instance].te, wt, ht, skFont, paint);
3417
                lines = textLines.size();
3418
            }
8 andreas 3419
 
69 andreas 3420
            MSG_DEBUG("Calculated number of lines: " << lines);
3421
            int lineHeight = (metrics.fAscent * -1) + metrics.fDescent;
67 andreas 3422
            int totalHeight = lineHeight * lines;
8 andreas 3423
 
67 andreas 3424
            if (totalHeight > ht)
3425
            {
3426
                lines = ht / lineHeight;
3427
                totalHeight = lineHeight * lines;
3428
            }
40 andreas 3429
 
69 andreas 3430
            MSG_DEBUG("Line height: " << lineHeight << ", total height: " << totalHeight);
3431
            vector<string>::iterator iter;
3432
            int line = 0;
3433
            int maxWidth = 0;
96 andreas 3434
 
3435
            if (textLines.size() > 0)
69 andreas 3436
            {
96 andreas 3437
                // Calculate the maximum width
118 andreas 3438
                for (iter = textLines.begin(); iter != textLines.end(); ++iter)
96 andreas 3439
                {
3440
                    SkRect rect;
3441
                    skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
40 andreas 3442
 
96 andreas 3443
                    if (rect.width() > maxWidth)
3444
                        maxWidth = rect.width();
3445
                }
69 andreas 3446
 
96 andreas 3447
                POSITION_t pos = calcImagePosition(maxWidth, totalHeight, SC_TEXT, instance);
69 andreas 3448
 
96 andreas 3449
                if (!pos.valid)
3450
                {
3451
                    MSG_ERROR("Error calculating the text position!");
3452
                    TError::setError();
3453
                    return false;
3454
                }
40 andreas 3455
 
96 andreas 3456
                SkScalar lnHt = metrics.fAscent * -1;
67 andreas 3457
 
118 andreas 3458
                for (iter = textLines.begin(); iter != textLines.end(); ++iter)
40 andreas 3459
                {
96 andreas 3460
                    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
3461
                    MSG_DEBUG("Triing to print line: " << *iter);
3462
                    // We want to take care about the horizontal position.
3463
                    SkRect rect;
3464
                    skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
3465
                    SkScalar horizontal = 0.0;
69 andreas 3466
 
96 andreas 3467
                    switch(sr[instance].jt)
3468
                    {
3469
                        case ORI_BOTTOM_MIDDLE:
3470
                        case ORI_CENTER_MIDDLE:
3471
                        case ORI_TOP_MIDDLE:
3472
                            horizontal = (wt - rect.width()) / 2.0f;
3473
                        break;
69 andreas 3474
 
96 andreas 3475
                        case ORI_BOTTOM_RIGHT:
3476
                        case ORI_CENTER_RIGHT:
3477
                        case ORI_TOP_RIGHT:
3478
                            horizontal = wt - rect.width();
3479
                        break;
40 andreas 3480
 
96 andreas 3481
                        default:
3482
                            horizontal = pos.left;
3483
                    }
8 andreas 3484
 
96 andreas 3485
                    SkScalar startX = horizontal;
3486
                    SkScalar startY = (SkScalar)pos.top + (SkScalar)lineHeight * (SkScalar)line;
3487
                    MSG_DEBUG("x=" << startX << ", y=" << startY);
3488
                    bool tEffect = false;
3489
                    // Text effects
3490
                    if (sr[instance].et > 0)
3491
                        tEffect = textEffect(&canvas, blob, startX, startY + lnHt, instance);
8 andreas 3492
 
96 andreas 3493
                    if (!tEffect)
3494
                        canvas.drawTextBlob(blob.get(), startX, startY + lnHt, paint);
8 andreas 3495
 
96 andreas 3496
                    line++;
3497
 
3498
                    if (line > lines)
3499
                        break;
3500
                }
67 andreas 3501
            }
3502
        }
3503
        else    // single line
3504
        {
3505
            string text = sr[instance].te;
3506
            sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.data(), skFont);
3507
            SkRect rect;
3508
            skFont.measureText(text.data(), text.size(), SkTextEncoding::kUTF8, &rect, &paint);
69 andreas 3509
            POSITION_t position = calcImagePosition(rect.width(), metrics.fCapHeight, SC_TEXT, instance);
66 andreas 3510
 
67 andreas 3511
            if (!position.valid)
3512
            {
3513
                MSG_ERROR("Error calculating the text position!");
3514
                TError::setError();
3515
                return false;
3516
            }
66 andreas 3517
 
69 andreas 3518
            MSG_DEBUG("Printing line " << text);
67 andreas 3519
            SkScalar startX = (SkScalar)position.left;
69 andreas 3520
            SkScalar startY = (SkScalar)position.top + metrics.fCapHeight;  // This is the offset of the line
10 andreas 3521
 
67 andreas 3522
            bool tEffect = false;
3523
            // Text effects
3524
            if (sr[instance].et > 0)
3525
                tEffect = textEffect(&canvas, blob, startX, startY, instance);
3526
 
3527
            if (!tEffect && utf8Strlen(text) > 1)
3528
                canvas.drawTextBlob(blob.get(), startX, startY, paint);
3529
            else
8 andreas 3530
            {
67 andreas 3531
                int count = 0;
3532
                uint16_t *glyphs = nullptr;
7 andreas 3533
 
67 andreas 3534
                if (TFont::isSymbol(typeFace))
8 andreas 3535
                {
67 andreas 3536
                    MSG_DEBUG("Symbol font detected.");
3537
                    uint16_t *uni;
3538
                    size_t num = TFont::utf8ToUtf16(text, &uni, true);
3539
                    MSG_DEBUG("Got " << num << " unichars, first unichar: " << std::hex << std::setw(4) << std::setfill('0') << *uni << std::dec);
8 andreas 3540
 
67 andreas 3541
                    if (num > 0)
3542
                    {
3543
                        glyphs = new uint16_t[num];
3544
                        size_t glyphSize = sizeof(uint16_t) * num;
3545
                        count = skFont.textToGlyphs(uni, num, SkTextEncoding::kUTF16, glyphs, glyphSize);
66 andreas 3546
 
67 andreas 3547
                        if (count <= 0)
3548
                        {
3549
                            delete[] glyphs;
3550
                            glyphs = TFont::textToGlyphs(text, typeFace, &num);
3551
                            count = num;
3552
                        }
3553
                    }
3554
                    else
3555
                    {
3556
                        canvas.drawTextBlob(blob.get(), startX, startY, paint);
3557
                        return true;
3558
                    }
66 andreas 3559
 
67 andreas 3560
                    if (uni)
3561
                        delete[] uni;
3562
                }
3563
                else
3564
                {
3565
                    glyphs = new uint16_t[text.size()];
3566
                    size_t glyphSize = sizeof(uint16_t) * text.size();
3567
                    count = skFont.textToGlyphs(text.data(), text.size(), SkTextEncoding::kUTF8, glyphs, glyphSize);
3568
                }
3569
 
3570
                MSG_DEBUG("1st glyph: 0x" << std::hex << std::setw(8) << std::setfill('0') << *glyphs << ", # glyphs: " << std::dec << count);
3571
                canvas.drawSimpleText(glyphs, sizeof(uint16_t) * count, SkTextEncoding::kGlyphID, startX, startY, skFont, paint);
3572
                delete[] glyphs;
8 andreas 3573
            }
3574
        }
3575
    }
38 andreas 3576
    else
3577
    {
67 andreas 3578
        MSG_WARNING("No font file name found for font " << sr[instance].fi);
38 andreas 3579
    }
8 andreas 3580
 
3581
    return true;
3582
}
3583
 
69 andreas 3584
int TButton::calcLineHeight(const string& text, SkFont& font)
40 andreas 3585
{
69 andreas 3586
    DECL_TRACER("TButton::calcLineHeight(const string& text, SkFont& font)");
40 andreas 3587
 
69 andreas 3588
    size_t pos = text.find("\n");       // Search for a line break.
3589
    string lText = text;
3590
 
3591
    if (pos != string::npos)            // Do we have found a line break?
3592
        lText = text.substr(0, pos - 1);// Yes, take only the text up to 1 before the line break (only 1 line).
3593
 
3594
    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(lText.c_str(), font);
40 andreas 3595
    SkRect rect = blob.get()->bounds();
3596
    return rect.height();
3597
}
3598
 
66 andreas 3599
bool TButton::textEffect(SkCanvas *canvas, sk_sp<SkTextBlob>& blob, SkScalar startX, SkScalar startY, int instance)
3600
{
3601
    DECL_TRACER("TButton::textEffect(SkBitmap *bm, int instance)");
3602
 
3603
    if (!canvas)
3604
        return false;
3605
 
3606
    // Drop Shadow
3607
    if (sr[instance].et >= 9 && sr[instance].et <= 32)
3608
    {
3609
        SkScalar gap = 0.0;
3610
        SkScalar sigma = 0.0;
3611
        SkScalar xDrop = 0.0;
3612
        SkScalar yDrop = 0.0;
3613
        uint8_t blurAlpha = 255;
3614
        SkPaint paint;
3615
        paint.setAntiAlias(true);
3616
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
3617
 
3618
        // Soft drop shadow
3619
        if (sr[instance].et >= 9 && sr[instance].et <= 16)
3620
        {
3621
            gap = (SkScalar)sr[instance].et - 8.0f;
3622
            sigma = 3.0f;
3623
            blurAlpha = 127;
3624
        }
3625
        else if (sr[instance].et >= 17 && sr[instance].et <= 24) // Medium drop shadow
3626
        {
3627
            gap = (SkScalar)sr[instance].et - 16.0f;
3628
            sigma = 2.0f;
3629
            blurAlpha = 159;
3630
        }
3631
        else    // Hard drop shadow
3632
        {
3633
            gap = (SkScalar)sr[instance].et - 24.0f;
3634
            sigma = 1.1f;
3635
            blurAlpha = 207;
3636
        }
3637
 
3638
        xDrop = gap;
3639
        yDrop = gap;
3640
        SkPaint blur(paint);
3641
        blur.setAlpha(blurAlpha);
3642
        blur.setColor(TColor::getSkiaColor(sr[instance].ec));
3643
        blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, 0));
3644
        canvas->drawTextBlob(blob.get(), startX + xDrop, startY + yDrop, blur);
3645
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
3646
        return true;
3647
    }
3648
 
3649
    return false;
3650
}
3651
 
8 andreas 3652
bool TButton::buttonBorder(SkBitmap* bm, int instance)
3653
{
3654
    DECL_TRACER("TButton::buttonBorder(SkBitmap* bm, int instance)");
3655
 
3656
    if (sr[instance].bs.empty())
3657
    {
3658
        MSG_DEBUG("No border defined.");
3659
        return true;
3660
    }
3661
 
79 andreas 3662
    // Try to find the border in the system table
3663
    BORDER_t bd;
81 andreas 3664
    int borderIndex = -1;
3665
    int i = 0;
79 andreas 3666
 
81 andreas 3667
    while (sysBorders[i].id)
79 andreas 3668
    {
101 andreas 3669
        string sysBrd = sysBorders[i].name;
3670
 
3671
        if (sysBrd == sr[instance].bs)
81 andreas 3672
        {
3673
            borderIndex = i;
3674
            break;
3675
        }
3676
 
3677
        i++;
3678
    }
3679
 
3680
    bool classExist = (gPageManager && gPageManager->getSystemDraw());
3681
 
3682
    if ((classExist && borderIndex >= 0 && !sysBorders[borderIndex].calc) || (classExist && borderIndex < 0))
3683
    {
79 andreas 3684
        if (gPageManager->getSystemDraw()->getBorder(sr[instance].bs, ((instance == 0) ? TSystemDraw::LT_OFF : TSystemDraw::LT_ON), &bd))
3685
        {
3686
            MSG_DEBUG("System border " << sr[instance].bs << " found.");
3687
 
81 andreas 3688
            SkColor color = TColor::getSkiaColor(sr[instance].cb);      // border color
3689
            SkColor bgColor = TColor::getSkiaColor(sr[instance].cf);    // fill color
79 andreas 3690
            // Load images
3691
            SkBitmap imgB, imgBR, imgR, imgTR, imgT, imgTL, imgL, imgBL;
3692
            sk_sp<SkData> image;
3693
 
3694
            if (!bd.b.empty() &&!(image = readImage(bd.b)))
3695
                return false;
3696
 
3697
            DecodeDataToBitmap(image, &imgB);
81 andreas 3698
            colorImage(&imgB, imgB.info().width(), imgB.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3699
            MSG_DEBUG("Got image " << bd.b << " with size " << imgB.info().width() << " x " << imgB.info().height());
79 andreas 3700
 
3701
            if (!bd.br.empty() && !(image = readImage(bd.br)))
3702
                return false;
3703
 
3704
            DecodeDataToBitmap(image, &imgBR);
81 andreas 3705
            colorImage(&imgBR, imgBR.info().width(), imgBR.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3706
            MSG_DEBUG("Got image " << bd.br << " with size " << imgBR.info().width() << " x " << imgBR.info().height());
79 andreas 3707
 
3708
            if (!bd.r.empty() && !(image = readImage(bd.r)))
3709
                return false;
3710
 
3711
            DecodeDataToBitmap(image, &imgR);
81 andreas 3712
            colorImage(&imgR, imgR.info().width(), imgR.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3713
            MSG_DEBUG("Got image " << bd.r << " with size " << imgR.info().width() << " x " << imgR.info().height());
79 andreas 3714
 
3715
            if (!bd.tr.empty() && !(image = readImage(bd.tr)))
3716
                return false;
3717
 
3718
            DecodeDataToBitmap(image, &imgTR);
81 andreas 3719
            colorImage(&imgTR, imgTR.info().width(), imgTR.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3720
            MSG_DEBUG("Got image " << bd.tr << " with size " << imgTR.info().width() << " x " << imgTR.info().height());
79 andreas 3721
 
3722
            if (!bd.t.empty() && !(image = readImage(bd.t)))
3723
                return false;
3724
 
3725
            DecodeDataToBitmap(image, &imgT);
81 andreas 3726
            colorImage(&imgT, imgT.info().width(), imgT.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3727
            MSG_DEBUG("Got image " << bd.t << " with size " << imgT.info().width() << " x " << imgT.info().height());
79 andreas 3728
 
3729
            if (!bd.tl.empty() && !(image = readImage(bd.tl)))
3730
                return false;
3731
 
3732
            DecodeDataToBitmap(image, &imgTL);
81 andreas 3733
            colorImage(&imgTL, imgTL.info().width(), imgTL.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3734
            MSG_DEBUG("Got image " << bd.tl << " with size " << imgTL.info().width() << " x " << imgTL.info().height());
79 andreas 3735
 
3736
            if (!bd.l.empty() && !(image = readImage(bd.l)))
3737
                return false;
3738
 
3739
            DecodeDataToBitmap(image, &imgL);
81 andreas 3740
            colorImage(&imgL, imgL.info().width(), imgL.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3741
            MSG_DEBUG("Got image " << bd.l << " with size " << imgL.info().width() << " x " << imgL.info().height());
79 andreas 3742
 
3743
            if (!bd.bl.empty() && !(image = readImage(bd.bl)))
3744
                return false;
3745
 
3746
            DecodeDataToBitmap(image, &imgBL);
81 andreas 3747
            colorImage(&imgBL, imgBL.info().width(), imgBL.info().height(), color, bgColor, (bd.border.fillLeft != bd.border.textLeft) ? true : false);
80 andreas 3748
            MSG_DEBUG("Got image " << bd.bl << " with size " << imgBL.info().width() << " x " << imgBL.info().height());
79 andreas 3749
 
80 andreas 3750
            MSG_DEBUG("Total size: " << wt << " x " << ht);
79 andreas 3751
            stretchImageWidth(&imgB, wt - imgBL.info().width() - imgBR.info().width());
3752
            stretchImageWidth(&imgT, wt - imgTL.info().width() - imgTR.info().width());
3753
            stretchImageHeight(&imgL, ht - imgTL.info().height() - imgBL.info().height());
3754
            stretchImageHeight(&imgR, ht - imgTR.info().height() - imgBR.info().height());
3755
            // Draw the frame
3756
            SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3757
            SkPaint paint;
3758
 
3759
            paint.setColor(color);
80 andreas 3760
            paint.setBlendMode(SkBlendMode::kSrcATop);
79 andreas 3761
            paint.setStyle(SkPaint::kStroke_Style);
3762
            canvas.drawBitmap(imgB, imgBL.info().width(), ht - imgB.info().height(), &paint);
3763
            canvas.drawBitmap(imgBR, wt - imgBR.info().width(), ht - imgBR.info().height(), &paint);
80 andreas 3764
            canvas.drawBitmap(imgR, wt - imgR.info().width(), imgTR.info().height(), &paint);
79 andreas 3765
            canvas.drawBitmap(imgTR, wt - imgTR.info().width(), 0, &paint);
3766
            canvas.drawBitmap(imgT, imgTL.info().width(), 0, &paint);
3767
            canvas.drawBitmap(imgTL, 0, 0, &paint);
3768
            canvas.drawBitmap(imgL, 0, imgTL.info().height(), &paint);
3769
            canvas.drawBitmap(imgBL, 0, ht - imgBL.info().height(), &paint);
3770
            return true;
3771
        }
3772
    }
3773
 
81 andreas 3774
    // Here we've not found the wanted border in the system table. Reasons may
3775
    // be a wrong name or the system images don't exist.
3776
    // We'll try to find it in our internal table. This is a fallback which
3777
    // provides only a limited number of system borders.
8 andreas 3778
 
81 andreas 3779
    if (borderIndex < 0)
3780
        return true;
3781
 
3782
    bool systemBrdExist = false;
3783
 
3784
    if (classExist && gPageManager->getSystemDraw()->getBorder(sr[instance].bs, ((instance == 0) ? TSystemDraw::LT_OFF : TSystemDraw::LT_ON), &bd))
3785
        systemBrdExist = true;
3786
 
3787
    if (sr[instance].bs.compare(sysBorders[borderIndex].name) == 0)
8 andreas 3788
    {
81 andreas 3789
        MSG_DEBUG("Border " << sysBorders[borderIndex].name << " found.");
3790
        SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3791
        SkPaint paint;
3792
        SkColor color = TColor::getSkiaColor(sr[instance].cb);
7 andreas 3793
 
81 andreas 3794
        paint.setColor(color);
3795
        paint.setBlendMode(SkBlendMode::kSrc);
3796
        paint.setStyle(SkPaint::kStroke_Style);
3797
        SkRRect outher, inner;
3798
        SkScalar radius = (SkScalar)sysBorders[borderIndex].radius;
3799
        int red, green, blue;
3800
        SkColor borderColor, bcLight, bcDark;
3801
        int lineWidth = 0;
8 andreas 3802
 
81 andreas 3803
        switch (sysBorders[borderIndex].id)
3804
        {
3805
            case 1: // Single Frame
3806
            case 2: // Double Frame
3807
            case 3: // Quad Frame
3808
                paint.setStrokeWidth(sysBorders[borderIndex].width);
3809
                canvas.drawRect(calcRect(wt, ht, sysBorders[borderIndex].width), paint);
3810
            break;
8 andreas 3811
 
81 andreas 3812
            case 4: // Picture Frame
3813
                {
3814
                    paint.setStrokeWidth(2);
3815
                    SkRect rect = SkRect::MakeXYWH(0, 0, wt, ht);
3816
                    canvas.drawRect(rect, paint);
3817
                    rect = SkRect::MakeXYWH(4, 4, wt - 4, ht - 4);
3818
                    canvas.drawRect(rect, paint);
3819
                }
3820
            break;
8 andreas 3821
 
81 andreas 3822
            case 5: // Circle 15
3823
            case 6: // Circle 25
3824
            case 7: // Circle 35
3825
            case 8: // Circle 45
3826
            case 9: // Circle 55
3827
            case 10: // Circle 65
3828
            case 11: // Circle 75
3829
            case 12: // Circle 85
3830
            case 13: // Circle 95
3831
            case 14: // Circle 105
3832
            case 15: // Circle 115
3833
            case 16: // Circle 125
3834
            case 17: // Circle 135
3835
            case 18: // Circle 145
3836
            case 19: // Circle 155
3837
            case 20: // Circle 165
3838
            case 21: // Circle 175
3839
            case 22: // Circle 185
3840
            case 23: // Circle 195
3841
                if (systemBrdExist)
3842
                {
3843
                    radius = (double)bd.border.idealWidth / 2.0;    // Take diameter and calculate radius
99 andreas 3844
                    lineWidth = bd.border.textLeft;
81 andreas 3845
                }
3846
                else
3847
                    lineWidth = sysBorders[borderIndex].width;
40 andreas 3848
 
101 andreas 3849
                paint.setStrokeWidth(1.0);
3850
                paint.setStyle(SkPaint::kFill_Style);
3851
                MSG_DEBUG("Line width: " << lineWidth << ", radius: " << radius);
3852
                // We draw a rounded rectangle to "clip" the corners. To do this
3853
                // in a way to not miss any pixel, we draw a rectangle followed
3854
                // by a rounded rectangle as an inner one. The space between
3855
                // them will be filled transparent.
81 andreas 3856
                outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
101 andreas 3857
                inner = SkRRect::MakeRectXY(calcRect(wt, ht, 1), radius, radius);
81 andreas 3858
                paint.setColor(SK_ColorTRANSPARENT);
3859
                canvas.drawDRRect(outher, inner, paint);
101 andreas 3860
                // Here we draw the rounded rectangle.
3861
                paint.setStyle(SkPaint::kStroke_Style);
3862
                paint.setStrokeWidth(lineWidth);
3863
                paint.setColor(color);
3864
                paint.setStrokeJoin(SkPaint::kRound_Join);
3865
                canvas.drawRoundRect(calcRect(wt, ht, lineWidth), radius, radius, paint);
3866
 
81 andreas 3867
            break;
40 andreas 3868
 
81 andreas 3869
            case 24:    // AMX Elite Inset -L
3870
            case 26:    // AMX Elite Inset -M
3871
            case 28:    // AMX Elite Inset -S
40 andreas 3872
                {
3873
                    borderColor = TColor::getSkiaColor(sr[instance].cb);
81 andreas 3874
                    vector<SkColor> cols = TColor::colorRange(borderColor, sysBorders[borderIndex].width, 40, TColor::DIR_LIGHT_DARK_LIGHT);
40 andreas 3875
                    vector<SkColor>::iterator iter;
3876
                    int i = 0;
3877
 
3878
                    for (iter = cols.begin(); iter != cols.end(); ++iter)
3879
                    {
3880
                        paint.setStrokeWidth(1);
3881
                        paint.setColor(*iter);
3882
                        SkRect rect = SkRect::MakeXYWH(i, i, wt - i, ht - i);
3883
                        canvas.drawRect(rect, paint);
3884
                        i++;
3885
                    }
3886
                }
81 andreas 3887
            break;
40 andreas 3888
 
81 andreas 3889
            case 25:    // AMX Elite Raised -L
3890
            case 27:    // AMX Elite Raised -M
3891
            case 29:    // AMX Elite Raised -S
3892
            {
3893
                borderColor = TColor::getSkiaColor(sr[instance].cb);
3894
                vector<SkColor> cols = TColor::colorRange(borderColor, sysBorders[borderIndex].width, 40, TColor::DIR_DARK_LIGHT_DARK);
3895
                vector<SkColor>::iterator iter;
3896
                int i = 0;
3897
 
3898
                for (iter = cols.begin(); iter != cols.end(); ++iter)
3899
                {
40 andreas 3900
                    paint.setStrokeWidth(1);
81 andreas 3901
                    paint.setColor(*iter);
3902
                    SkRect rect = SkRect::MakeXYWH(i, i, wt - i, ht - i);
3903
                    canvas.drawRect(rect, paint);
3904
                    i++;
3905
                }
3906
            }
3907
            break;
40 andreas 3908
 
81 andreas 3909
            case 30:    // BevelInset -L
3910
            case 32:    // Bevel Inset -M
3911
            case 34:    // Bevel Inset -S
3912
                borderColor = TColor::getSkiaColor(sr[instance].cb);
3913
                red = std::min((int)SkColorGetR(borderColor) + 20, 255);
3914
                green = std::min((int)SkColorGetG(borderColor) + 20, 255);
3915
                blue = std::min((int)SkColorGetB(borderColor) + 20, 255);
3916
                bcLight = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
3917
                red = std::max((int)SkColorGetR(borderColor) - 20, 0);
3918
                green = std::max((int)SkColorGetG(borderColor) - 20, 0);
3919
                blue = std::max((int)SkColorGetB(borderColor) - 20, 0);
3920
                bcDark = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
3921
                paint.setStrokeWidth(1);
3922
                paint.setColor(bcDark);
3923
                // Lines on the left
3924
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3925
                {
3926
                    int yt = i;
3927
                    int yb = ht - i;
3928
                    canvas.drawLine(i, yt, i, yb, paint);
3929
                }
3930
                // Lines on the top
3931
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3932
                {
3933
                    int xl = i;
3934
                    int xr = wt - i;
3935
                    canvas.drawLine(xl, i, xr, i, paint);
3936
                }
3937
                // Lines on right side
3938
                paint.setColor(bcLight);
40 andreas 3939
 
81 andreas 3940
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3941
                {
3942
                    int yt = i;
3943
                    int yb = ht - i;
3944
                    canvas.drawLine(wt - i, yt, wt - i, yb, paint);
3945
                }
3946
                // Lines on bottom
3947
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3948
                {
3949
                    int xl = i;
3950
                    int xr = wt - i;
3951
                    canvas.drawLine(xl, ht - i, xr, ht - i, paint);
3952
                }
3953
            break;
40 andreas 3954
 
81 andreas 3955
            case 31:    // Bevel Raised _L
3956
            case 33:    // Bevel Raised _M
3957
            case 35:    // Bevel Raised _S
3958
                borderColor = TColor::getSkiaColor(sr[instance].cb);
3959
                red = std::min((int)SkColorGetR(borderColor) + 10, 255);
3960
                green = std::min((int)SkColorGetG(borderColor) + 10, 255);
3961
                blue = std::min((int)SkColorGetB(borderColor) + 10, 255);
3962
                bcLight = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
3963
                red = std::max((int)SkColorGetR(borderColor) - 10, 0);
3964
                green = std::max((int)SkColorGetG(borderColor) - 10, 0);
3965
                blue = std::max((int)SkColorGetB(borderColor) - 10, 0);
3966
                bcDark = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
3967
                paint.setStrokeWidth(1);
3968
                paint.setColor(bcLight);
3969
                // Lines on the left
3970
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3971
                {
3972
                    int yt = i;
3973
                    int yb = ht - i;
3974
                    canvas.drawLine(i, yt, i, yb, paint);
3975
                }
3976
                // Lines on the top
3977
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3978
                {
3979
                    int xl = i;
3980
                    int xr = wt - i;
3981
                    canvas.drawLine(xl, i, xr, i, paint);
3982
                }
3983
                // Lines on right side
3984
                paint.setColor(bcDark);
3985
 
3986
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3987
                {
3988
                    int yt = i;
3989
                    int yb = ht - i;
3990
                    canvas.drawLine(wt - i, yt, wt - i, yb, paint);
3991
                }
3992
                // Lines on bottom
3993
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
3994
                {
3995
                    int xl = i;
3996
                    int xr = wt - borderIndex;
3997
                    canvas.drawLine(xl, ht - i, xr, ht - i, paint);
3998
                }
3999
            break;
6 andreas 4000
        }
4 andreas 4001
    }
4002
 
3 andreas 4003
    return true;
4004
}
4005
 
8 andreas 4006
int TButton::numberLines(const string& str)
4007
{
4008
    DECL_TRACER("TButton::numberLines(const string& str)");
4009
 
4010
    int lines = 1;
4011
 
96 andreas 4012
    if (str.empty())
8 andreas 4013
    {
96 andreas 4014
        MSG_DEBUG("Found an empty string.");
4015
        return lines;
4016
    }
4017
 
4018
    string::const_iterator iter;
4019
 
118 andreas 4020
    for (iter = str.begin(); iter != str.end(); ++iter)
96 andreas 4021
    {
4022
        if (*iter == '\n')
8 andreas 4023
            lines++;
4024
    }
4025
 
4026
    return lines;
4027
}
4028
 
4029
SkRect TButton::calcRect(int width, int height, int pen)
4030
{
4031
    DECL_TRACER("TButton::calcRect(int width, int height, int pen)");
4032
    SkRect rect;
4033
 
4034
    SkScalar left = (SkScalar)pen / 2.0;
4035
    SkScalar top = (SkScalar)pen / 2.0;
101 andreas 4036
    SkScalar w = (SkScalar)width - (SkScalar)pen;
4037
    SkScalar h = (SkScalar)height - (SkScalar)pen;
8 andreas 4038
    rect.setXYWH(left, top, w, h);
4039
    return rect;
4040
}
4041
 
15 andreas 4042
void TButton::runAnimation()
8 andreas 4043
{
15 andreas 4044
    DECL_TRACER("TButton::runAnimation()");
8 andreas 4045
 
43 andreas 4046
    if (mAniRunning)
4047
        return;
4048
 
15 andreas 4049
    mAniRunning = true;
4050
    int instance = 0;
4051
    int max = (int)sr.size();
38 andreas 4052
    ulong tm = nu * ru + nd * rd;
15 andreas 4053
 
93 andreas 4054
    while (mAniRunning && !mAniStop && !prg_stopped)
15 andreas 4055
    {
38 andreas 4056
        mActInstance = instance;
4057
 
101 andreas 4058
        if (visible && !drawButton(instance))
15 andreas 4059
            break;
4060
 
4061
        instance++;
4062
 
4063
        if (instance >= max)
4064
            instance = 0;
4065
 
4066
        std::this_thread::sleep_for(std::chrono::milliseconds(tm));
4067
    }
4068
 
4069
    mAniRunning = false;
4070
}
4071
 
38 andreas 4072
void TButton::runAnimationRange(int start, int end, ulong step)
4073
{
4074
    DECL_TRACER("TButton::runAnimationRange(int start, int end, ulong step)");
4075
 
93 andreas 4076
    if (mAniRunning)
4077
        return;
4078
 
38 andreas 4079
    mAniRunning = true;
4080
    int instance = start;
4081
    int max = std::min(end, (int)sr.size());
4082
    std::chrono::steady_clock::time_point startt = std::chrono::steady_clock::now();
4083
 
93 andreas 4084
    while (mAniRunning && !mAniStop && !prg_stopped)
38 andreas 4085
    {
4086
        mActInstance = instance;
101 andreas 4087
 
4088
        if (visible)
4089
            drawButton(instance);   // We ignore the state and try to draw the next instance
4090
 
38 andreas 4091
        instance++;
4092
 
4093
        if (instance >= max)
4094
            instance = start;
4095
 
4096
        std::this_thread::sleep_for(std::chrono::milliseconds(step));
4097
 
4098
        if (mAniRunTime > 0)
4099
        {
4100
            std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
4101
            std::chrono::nanoseconds difftime = current - startt;
4102
            ulong duration = std::chrono::duration_cast<std::chrono::milliseconds>(difftime).count();
4103
 
4104
            if (duration >= mAniRunTime)
4105
                break;
4106
        }
4107
    }
4108
 
4109
    mAniRunTime = 0;
4110
    mAniRunning = false;
4111
}
4112
 
15 andreas 4113
bool TButton::drawButtonMultistateAni()
4114
{
101 andreas 4115
    DECL_TRACER("TButton::drawButtonMultistateAni()");
15 andreas 4116
 
33 andreas 4117
    if (prg_stopped)
4118
        return true;
35 andreas 4119
 
100 andreas 4120
    if (!visible || hd)    // Do nothing if this button is invisible
4121
        return true;
4122
 
15 andreas 4123
    if (mAniRunning || mThrAni.joinable())
4124
    {
23 andreas 4125
        MSG_TRACE("Animation is already running!");
15 andreas 4126
        return true;
4127
    }
4128
 
4129
    try
4130
    {
93 andreas 4131
        mAniStop = false;
15 andreas 4132
        mThrAni = thread([=] { runAnimation(); });
4133
        mThrAni.detach();
4134
    }
4135
    catch (exception& e)
4136
    {
4137
        MSG_ERROR("Error starting the button animation thread: " << e.what());
4138
        return false;
4139
    }
4140
 
4141
    return true;
4142
}
4143
 
4144
bool TButton::drawButton(int instance, bool show)
4145
{
4146
    mutex_button.lock();
4147
    DECL_TRACER("TButton::drawButton(int instance, bool show)");
4148
 
33 andreas 4149
    if (prg_stopped)
34 andreas 4150
    {
4151
        mutex_button.unlock();
33 andreas 4152
        return false;
34 andreas 4153
    }
35 andreas 4154
 
8 andreas 4155
    if ((size_t)instance >= sr.size() || instance < 0)
4156
    {
4157
        MSG_ERROR("Instance " << instance << " is out of bounds!");
4158
        TError::setError();
15 andreas 4159
        mutex_button.unlock();
8 andreas 4160
        return false;
4161
    }
4162
 
26 andreas 4163
    if (!_displayButton && gPageManager)
4164
        _displayButton = gPageManager->getCallbackDB();
4165
 
100 andreas 4166
    if (!visible || hd || instance != mActInstance || !_displayButton)
14 andreas 4167
    {
15 andreas 4168
        bool db = (_displayButton != nullptr);
23 andreas 4169
        MSG_DEBUG("Button " << bi << ", \"" << na << "\" at instance " << instance << " is not to draw!");
15 andreas 4170
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
4171
        mutex_button.unlock();
14 andreas 4172
        return true;
4173
    }
4174
 
8 andreas 4175
    ulong parent = mHandle & 0xffff0000;
4176
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
4177
 
4178
    if (TError::isError())
15 andreas 4179
    {
4180
        mutex_button.unlock();
8 andreas 4181
        return false;
15 andreas 4182
    }
8 andreas 4183
 
4184
    SkBitmap imgButton;
10 andreas 4185
    imgButton.allocN32Pixels(wt, ht);
97 andreas 4186
    bool dynState = false;
8 andreas 4187
 
4188
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
4189
    {
4190
        if (mDOrder[i] == ORD_ELEM_FILL)
4191
        {
4192
            if (!buttonFill(&imgButton, instance))
15 andreas 4193
            {
4194
                mutex_button.unlock();
8 andreas 4195
                return false;
15 andreas 4196
            }
8 andreas 4197
        }
4198
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
4199
        {
21 andreas 4200
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
15 andreas 4201
            {
97 andreas 4202
                MSG_DEBUG("Button reported an error. Abort drawing!");
15 andreas 4203
                mutex_button.unlock();
8 andreas 4204
                return false;
15 andreas 4205
            }
97 andreas 4206
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, show, &dynState))
21 andreas 4207
            {
97 andreas 4208
                MSG_DEBUG("Dynamic button reported an error. Abort drawing!");
21 andreas 4209
                mutex_button.unlock();
4210
                return false;
4211
            }
8 andreas 4212
        }
4213
        else if (mDOrder[i] == ORD_ELEM_ICON)
4214
        {
4215
            if (!buttonIcon(&imgButton, instance))
15 andreas 4216
            {
4217
                mutex_button.unlock();
8 andreas 4218
                return false;
15 andreas 4219
            }
8 andreas 4220
        }
4221
        else if (mDOrder[i] == ORD_ELEM_TEXT)
4222
        {
4223
            if (!buttonText(&imgButton, instance))
15 andreas 4224
            {
4225
                mutex_button.unlock();
8 andreas 4226
                return false;
15 andreas 4227
            }
8 andreas 4228
        }
4229
        else if (mDOrder[i] == ORD_ELEM_BORDER)
4230
        {
4231
            if (!buttonBorder(&imgButton, instance))
15 andreas 4232
            {
4233
                mutex_button.unlock();
8 andreas 4234
                return false;
15 andreas 4235
            }
8 andreas 4236
        }
4237
    }
4238
 
10 andreas 4239
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
4240
    {
4241
        SkBitmap ooButton;
4242
        int w = imgButton.width();
4243
        int h = imgButton.height();
4244
        ooButton.allocN32Pixels(w, h, true);
4245
        SkCanvas canvas(ooButton);
4246
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
4247
        SkRegion region;
4248
        region.setRect(irect);
4249
        SkScalar oo;
4250
 
4251
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
4252
        {
4253
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
4254
            MSG_DEBUG("Set global overal opacity to " << oo);
4255
        }
4256
        else if (sr[instance].oo >= 0)
4257
        {
4258
            oo = (SkScalar)sr[instance].oo;
4259
            MSG_DEBUG("Set overal opacity to " << oo);
4260
        }
4261
        else
4262
        {
4263
            oo = (SkScalar)mGlobalOO;
4264
            MSG_DEBUG("Set global overal opacity to " << oo);
4265
        }
4266
 
4267
        SkScalar alpha = 1.0 / 255.0 * oo;
4268
        MSG_DEBUG("Calculated alpha value: " << alpha);
4269
        SkPaint paint;
4270
        paint.setAlphaf(alpha);
4271
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
4272
        canvas.drawBitmap(imgButton, 0, 0, &paint);
4273
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
4274
        imgButton = ooButton;
4275
    }
4276
 
15 andreas 4277
    mLastImage = imgButton;
8 andreas 4278
    size_t rowBytes = imgButton.info().minRowBytes();
4279
 
97 andreas 4280
    if (!prg_stopped && !dynState)
26 andreas 4281
    {
4282
        int rwidth = wt;
4283
        int rheight = ht;
4284
        int rleft = lt;
4285
        int rtop = tp;
43 andreas 4286
#ifdef _SCALE_SKIA_
26 andreas 4287
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
4288
        {
4289
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
4290
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
4291
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
4292
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
4293
 
4294
            SkPaint paint;
4295
            paint.setBlendMode(SkBlendMode::kSrc);
4296
            paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 4297
            // Calculate new dimension
26 andreas 4298
            SkImageInfo info = imgButton.info();
4299
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
4300
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
28 andreas 4301
            // Create a canvas and draw new image
4302
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
4303
            imgButton.allocN32Pixels(width, height);
4304
            imgButton.eraseColor(SK_ColorTRANSPARENT);
26 andreas 4305
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4306
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
4307
            can.drawImageRect(im, rect, &paint);
28 andreas 4308
            rowBytes = imgButton.info().minRowBytes();
4309
            mLastImage = imgButton;
26 andreas 4310
        }
43 andreas 4311
#endif
31 andreas 4312
        if (show)
4313
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
26 andreas 4314
    }
4315
 
15 andreas 4316
    mutex_button.unlock();
8 andreas 4317
    return true;
4318
}
4319
 
50 andreas 4320
bool TButton::drawTextArea(int instance)
4321
{
4322
    DECL_TRACER("TButton::drawTextArea(int instance, bool show)");
4323
 
4324
    if (prg_stopped)
4325
        return false;
4326
 
100 andreas 4327
    if (!visible || hd)
4328
        return true;
4329
 
50 andreas 4330
    if ((size_t)instance >= sr.size() || instance < 0)
4331
    {
4332
        MSG_ERROR("Instance " << instance << " is out of bounds!");
4333
        TError::setError();
4334
        return false;
4335
    }
4336
 
4337
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
4338
 
4339
    if (TError::isError())
4340
        return false;
4341
 
4342
    SkBitmap imgButton;
4343
    imgButton.allocN32Pixels(wt, ht);
4344
 
4345
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
4346
    {
4347
        if (mDOrder[i] == ORD_ELEM_FILL)
4348
        {
4349
            if (!buttonFill(&imgButton, instance))
4350
                return false;
4351
        }
4352
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
4353
        {
4354
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
4355
                return false;
97 andreas 4356
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, false))
50 andreas 4357
                return false;
4358
        }
4359
        else if (mDOrder[i] == ORD_ELEM_ICON)
4360
        {
4361
            if (!buttonIcon(&imgButton, instance))
4362
                return false;
4363
        }
4364
        else if (mDOrder[i] == ORD_ELEM_BORDER)
4365
        {
4366
            if (!buttonBorder(&imgButton, instance))
4367
                return false;
4368
        }
4369
    }
4370
 
4371
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
4372
    {
4373
        SkBitmap ooButton;
4374
        int w = imgButton.width();
4375
        int h = imgButton.height();
4376
        ooButton.allocN32Pixels(w, h, true);
4377
        SkCanvas canvas(ooButton);
4378
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
4379
        SkRegion region;
4380
        region.setRect(irect);
4381
        SkScalar oo;
4382
 
4383
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
4384
        {
4385
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
4386
            MSG_DEBUG("Set global overal opacity to " << oo);
4387
        }
4388
        else if (sr[instance].oo >= 0)
4389
        {
4390
            oo = (SkScalar)sr[instance].oo;
4391
            MSG_DEBUG("Set overal opacity to " << oo);
4392
        }
4393
        else
4394
        {
4395
            oo = (SkScalar)mGlobalOO;
4396
            MSG_DEBUG("Set global overal opacity to " << oo);
4397
        }
4398
 
4399
        SkScalar alpha = 1.0 / 255.0 * oo;
4400
        MSG_DEBUG("Calculated alpha value: " << alpha);
4401
        SkPaint paint;
4402
        paint.setAlphaf(alpha);
4403
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
4404
        canvas.drawBitmap(imgButton, 0, 0, &paint);
4405
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
4406
        imgButton = ooButton;
4407
    }
4408
 
4409
    mLastImage = imgButton;
4410
 
52 andreas 4411
    if (!prg_stopped)
50 andreas 4412
    {
4413
        int rwidth = wt;
4414
        int rheight = ht;
4415
        int rleft = lt;
4416
        int rtop = tp;
4417
        size_t rowBytes = imgButton.info().minRowBytes();
52 andreas 4418
#ifdef _SCALE_SKIA_
4419
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
4420
        {
4421
            size_t rowBytes = imgButton.info().minRowBytes();
4422
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
4423
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
4424
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
4425
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
50 andreas 4426
 
52 andreas 4427
            SkPaint paint;
4428
            paint.setBlendMode(SkBlendMode::kSrc);
4429
            paint.setFilterQuality(kHigh_SkFilterQuality);
4430
            // Calculate new dimension
4431
            SkImageInfo info = imgButton.info();
4432
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
4433
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
4434
            // Create a canvas and draw new image
4435
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
4436
            imgButton.allocN32Pixels(width, height);
4437
            imgButton.eraseColor(SK_ColorTRANSPARENT);
4438
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4439
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
4440
            can.drawImageRect(im, rect, &paint);
4441
            rowBytes = imgButton.info().minRowBytes();
4442
            mLastImage = imgButton;
4443
        }
4444
#endif
4445
        if (gPageManager && gPageManager->getCallbackInputText())
4446
        {
4447
            BITMAP_t bm;
4448
            bm.buffer = (unsigned char *)imgButton.getPixels();
4449
            bm.rowBytes = rowBytes;
4450
            bm.left = rleft;
4451
            bm.top = rtop;
4452
            bm.width = rwidth;
4453
            bm.height = rheight;
4454
            gPageManager->getCallbackInputText()(this, bm);
4455
        }
50 andreas 4456
    }
4457
 
4458
    return true;
4459
}
4460
 
38 andreas 4461
bool TButton::drawMultistateBargraph(int level, bool show)
4462
{
4463
    mutex_button.lock();
4464
    DECL_TRACER("TButton::drawMultistateBargraph(int level, bool show)");
4465
 
4466
    if (prg_stopped)
4467
    {
4468
        mutex_button.unlock();
4469
        return false;
4470
    }
4471
 
4472
    if (!_displayButton && gPageManager)
4473
        _displayButton = gPageManager->getCallbackDB();
4474
 
100 andreas 4475
    if (!visible || hd || !_displayButton)
38 andreas 4476
    {
4477
        bool db = (_displayButton != nullptr);
4478
        MSG_DEBUG("Multistate bargraph " << bi << ", \"" << na << " is not to draw!");
4479
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
4480
        mutex_button.unlock();
4481
        return true;
4482
    }
4483
 
4484
    int maxLevel = level;
4485
 
4486
    if (maxLevel >= rh)
4487
        maxLevel = rh - 1 ;
45 andreas 4488
    else if (maxLevel <= rl && rl > 0)
38 andreas 4489
        maxLevel = rl - 1;
45 andreas 4490
    else if (maxLevel < 0)
4491
        maxLevel = 0;
38 andreas 4492
 
46 andreas 4493
    MSG_DEBUG("Display instance " << maxLevel);
38 andreas 4494
    ulong parent = mHandle & 0xffff0000;
4495
    getDrawOrder(sr[maxLevel]._do, (DRAW_ORDER *)&mDOrder);
4496
 
4497
    if (TError::isError())
4498
    {
4499
        mutex_button.unlock();
4500
        return false;
4501
    }
4502
 
4503
    SkBitmap imgButton;
4504
    imgButton.allocN32Pixels(wt, ht);
4505
 
4506
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
4507
    {
4508
        if (mDOrder[i] == ORD_ELEM_FILL)
4509
        {
4510
            if (!buttonFill(&imgButton, maxLevel))
4511
            {
4512
                mutex_button.unlock();
4513
                return false;
4514
            }
4515
        }
4516
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
4517
        {
4518
            if (!buttonBitmap(&imgButton, maxLevel))
4519
            {
4520
                mutex_button.unlock();
4521
                return false;
4522
            }
4523
        }
4524
        else if (mDOrder[i] == ORD_ELEM_ICON)
4525
        {
4526
            if (!buttonIcon(&imgButton, maxLevel))
4527
            {
4528
                mutex_button.unlock();
4529
                return false;
4530
            }
4531
        }
4532
        else if (mDOrder[i] == ORD_ELEM_TEXT)
4533
        {
4534
            if (!buttonText(&imgButton, maxLevel))
4535
            {
4536
                mutex_button.unlock();
4537
                return false;
4538
            }
4539
        }
4540
        else if (mDOrder[i] == ORD_ELEM_BORDER)
4541
        {
4542
            if (!buttonBorder(&imgButton, maxLevel))
4543
            {
4544
                mutex_button.unlock();
4545
                return false;
4546
            }
4547
        }
4548
    }
4549
 
4550
    if (mGlobalOO >= 0 || sr[maxLevel].oo >= 0) // Take overall opacity into consideration
4551
    {
4552
        SkBitmap ooButton;
4553
        int w = imgButton.width();
4554
        int h = imgButton.height();
4555
        ooButton.allocN32Pixels(w, h, true);
4556
        SkCanvas canvas(ooButton);
4557
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
4558
        SkRegion region;
4559
        region.setRect(irect);
4560
        SkScalar oo;
4561
 
4562
        if (mGlobalOO >= 0 && sr[maxLevel].oo >= 0)
4563
        {
4564
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[maxLevel].oo);
4565
            MSG_DEBUG("Set global overal opacity to " << oo);
4566
        }
4567
        else if (sr[maxLevel].oo >= 0)
4568
        {
4569
            oo = (SkScalar)sr[maxLevel].oo;
4570
            MSG_DEBUG("Set overal opacity to " << oo);
4571
        }
4572
        else
4573
        {
4574
            oo = (SkScalar)mGlobalOO;
4575
            MSG_DEBUG("Set global overal opacity to " << oo);
4576
        }
4577
 
4578
        SkScalar alpha = 1.0 / 255.0 * oo;
4579
        MSG_DEBUG("Calculated alpha value: " << alpha);
4580
        SkPaint paint;
4581
        paint.setAlphaf(alpha);
4582
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
4583
        canvas.drawBitmap(imgButton, 0, 0, &paint);
4584
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
4585
        imgButton = ooButton;
4586
    }
4587
 
4588
    mLastImage = imgButton;
4589
    size_t rowBytes = imgButton.info().minRowBytes();
4590
 
4591
    if (!prg_stopped)
4592
    {
4593
        int rwidth = wt;
4594
        int rheight = ht;
4595
        int rleft = lt;
4596
        int rtop = tp;
43 andreas 4597
#ifdef _SCALE_SKIA_
38 andreas 4598
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
4599
        {
4600
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
4601
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
4602
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
4603
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
4604
 
4605
            SkPaint paint;
4606
            paint.setBlendMode(SkBlendMode::kSrc);
4607
            paint.setFilterQuality(kHigh_SkFilterQuality);
4608
            // Calculate new dimension
4609
            SkImageInfo info = imgButton.info();
4610
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
4611
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
4612
            MSG_DEBUG("Button dimension: " << width << " x " << height);
4613
            // Create a canvas and draw new image
4614
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
4615
            imgButton.allocN32Pixels(width, height);
4616
            imgButton.eraseColor(SK_ColorTRANSPARENT);
4617
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4618
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
4619
            can.drawImageRect(im, rect, &paint);
4620
            MSG_DEBUG("Old rowBytes: " << rowBytes);
4621
            rowBytes = imgButton.info().minRowBytes();
4622
            MSG_DEBUG("New rowBytes: " << rowBytes);
4623
            mLastImage = imgButton;
4624
        }
43 andreas 4625
#endif
38 andreas 4626
        if (show)
4627
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
4628
    }
4629
 
4630
    mutex_button.unlock();
4631
    return true;
4632
}
4633
 
15 andreas 4634
bool TButton::drawBargraph(int instance, int level, bool show)
4635
{
4636
    mutex_bargraph.lock();
4637
    DECL_TRACER("TButton::drawBargraph(int instance, bool show)");
4638
 
4639
    if ((size_t)instance >= sr.size() || instance < 0)
4640
    {
4641
        MSG_ERROR("Instance " << instance << " is out of bounds!");
4642
        TError::setError();
4643
        mutex_bargraph.unlock();
4644
        return false;
4645
    }
4646
 
38 andreas 4647
    if (!_displayButton && gPageManager)
4648
        _displayButton = gPageManager->getCallbackDB();
4649
 
15 andreas 4650
    if (level < rl)
4651
        mLastLevel = rl;
4652
    else if (level > rh)
4653
        mLastLevel = rh;
4654
    else
4655
        mLastLevel = level;
4656
 
38 andreas 4657
    int inst = instance;
4658
 
100 andreas 4659
    if (!visible || hd || instance != mActInstance || !_displayButton)
15 andreas 4660
    {
4661
        bool db = (_displayButton != nullptr);
23 andreas 4662
        MSG_DEBUG("Bargraph " << bi << ", \"" << na << "\" at instance " << instance << " with level " << mLastLevel << " is not to draw!");
15 andreas 4663
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
4664
        mutex_bargraph.unlock();
4665
        return true;
4666
    }
4667
 
4668
    ulong parent = mHandle & 0xffff0000;
35 andreas 4669
 
20 andreas 4670
    if (type == BARGRAPH)
38 andreas 4671
    {
20 andreas 4672
        getDrawOrder(sr[1]._do, (DRAW_ORDER *)&mDOrder);
38 andreas 4673
        inst = 1;
4674
    }
20 andreas 4675
    else
4676
        getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
35 andreas 4677
 
15 andreas 4678
    if (TError::isError())
4679
    {
4680
        mutex_bargraph.unlock();
4681
        return false;
4682
    }
4683
 
4684
    SkBitmap imgButton;
4685
    imgButton.allocN32Pixels(wt, ht);
38 andreas 4686
    imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
4687
    bool haveFrame = false;
15 andreas 4688
 
4689
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
4690
    {
38 andreas 4691
        if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
15 andreas 4692
        {
38 andreas 4693
            if (!buttonFill(&imgButton, (type == BARGRAPH ? 0 : inst)))
15 andreas 4694
            {
4695
                mutex_bargraph.unlock();
4696
                return false;
4697
            }
4698
        }
4699
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
4700
        {
38 andreas 4701
            if (!barLevel(&imgButton, inst, mLastLevel))
15 andreas 4702
            {
4703
                mutex_bargraph.unlock();
4704
                return false;
4705
            }
4706
        }
4707
        else if (mDOrder[i] == ORD_ELEM_ICON)
4708
        {
38 andreas 4709
            if (!buttonIcon(&imgButton, inst))
15 andreas 4710
            {
4711
                mutex_bargraph.unlock();
4712
                return false;
4713
            }
4714
        }
4715
        else if (mDOrder[i] == ORD_ELEM_TEXT)
4716
        {
38 andreas 4717
            if (!buttonText(&imgButton, inst))
15 andreas 4718
            {
4719
                mutex_bargraph.unlock();
4720
                return false;
4721
            }
4722
        }
4723
        else if (mDOrder[i] == ORD_ELEM_BORDER)
4724
        {
38 andreas 4725
            if (!buttonBorder(&imgButton, (type == BARGRAPH ? 0 : inst)))
15 andreas 4726
            {
4727
                mutex_bargraph.unlock();
4728
                return false;
4729
            }
38 andreas 4730
 
4731
            haveFrame = true;
15 andreas 4732
        }
4733
    }
4734
 
38 andreas 4735
    if (mGlobalOO >= 0 || sr[inst].oo >= 0) // Take overall opacity into consideration
15 andreas 4736
    {
4737
        SkBitmap ooButton;
4738
        int w = imgButton.width();
4739
        int h = imgButton.height();
4740
        ooButton.allocN32Pixels(w, h, true);
4741
        SkCanvas canvas(ooButton);
4742
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
4743
        SkRegion region;
4744
        region.setRect(irect);
4745
        SkScalar oo;
4746
 
38 andreas 4747
        if (mGlobalOO >= 0 && sr[inst].oo >= 0)
15 andreas 4748
        {
38 andreas 4749
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[inst].oo);
15 andreas 4750
            MSG_DEBUG("Set global overal opacity to " << oo);
4751
        }
38 andreas 4752
        else if (sr[inst].oo >= 0)
15 andreas 4753
        {
38 andreas 4754
            oo = (SkScalar)sr[inst].oo;
15 andreas 4755
            MSG_DEBUG("Set overal opacity to " << oo);
4756
        }
4757
        else
4758
        {
4759
            oo = (SkScalar)mGlobalOO;
4760
            MSG_DEBUG("Set global overal opacity to " << oo);
4761
        }
4762
 
4763
        SkScalar alpha = 1.0 / 255.0 * oo;
4764
        MSG_DEBUG("Calculated alpha value: " << alpha);
4765
        SkPaint paint;
4766
        paint.setAlphaf(alpha);
4767
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
4768
        canvas.drawBitmap(imgButton, 0, 0, &paint);
4769
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
4770
        imgButton = ooButton;
4771
    }
4772
 
4773
    mLastImage = imgButton;
4774
    size_t rowBytes = imgButton.info().minRowBytes();
4775
 
21 andreas 4776
    if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
26 andreas 4777
    {
4778
        int rwidth = wt;
4779
        int rheight = ht;
4780
        int rleft = lt;
4781
        int rtop = tp;
43 andreas 4782
#ifdef _SCALE_SKIA_
26 andreas 4783
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
4784
        {
4785
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
4786
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
4787
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
4788
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
4789
 
4790
            SkPaint paint;
4791
            paint.setBlendMode(SkBlendMode::kSrc);
4792
            paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 4793
            // Calculate new dimension
26 andreas 4794
            SkImageInfo info = imgButton.info();
4795
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
4796
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
28 andreas 4797
            // Create a canvas and draw new image
4798
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
4799
            imgButton.allocN32Pixels(width, height);
4800
            imgButton.eraseColor(SK_ColorTRANSPARENT);
26 andreas 4801
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4802
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
4803
            can.drawImageRect(im, rect, &paint);
28 andreas 4804
            rowBytes = imgButton.info().minRowBytes();
4805
            mLastImage = imgButton;
26 andreas 4806
        }
43 andreas 4807
#endif
26 andreas 4808
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
4809
    }
4810
 
15 andreas 4811
    mutex_bargraph.unlock();
4812
    return true;
4813
}
4814
 
40 andreas 4815
POSITION_t TButton::calcImagePosition(int width, int height, CENTER_CODE cc, int number, int line)
4 andreas 4816
{
4817
    DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
4818
 
4819
    SR_T act_sr;
4820
    POSITION_t position;
69 andreas 4821
    int ix, iy, ln;
4 andreas 4822
 
4823
    if (sr.size() == 0)
4824
        return position;
4825
 
4826
    if (number <= 0)
4827
        act_sr = sr.at(0);
4828
    else if ((size_t)number < sr.size())
4829
        act_sr = sr.at(number);
46 andreas 4830
    else if ((size_t)number >= sr.size())
4831
        act_sr = sr.at(sr.size() - 1);
4 andreas 4832
    else
4833
        return position;
4834
 
69 andreas 4835
    if (line <= 0)
4836
        ln = 1;
4837
    else
4838
        ln = line;
4839
 
4 andreas 4840
    int border_size = getBorderSize(act_sr.bs);
8 andreas 4841
    int code, border = border_size;
10 andreas 4842
    string dbgCC;
17 andreas 4843
    int rwt = 0, rht = 0;
4 andreas 4844
 
4845
    switch (cc)
3 andreas 4846
    {
8 andreas 4847
        case SC_ICON:
4848
            code = act_sr.ji;
4849
            ix = act_sr.ix;
4850
            iy = act_sr.iy;
4851
            border = border_size = 0;
10 andreas 4852
            dbgCC = "ICON";
17 andreas 4853
            rwt = width;
4854
            rht = height;
8 andreas 4855
        break;
4856
 
4857
        case SC_BITMAP:
4858
            code = act_sr.jb;
10 andreas 4859
            ix = act_sr.bx;
4860
            iy = act_sr.by;
4861
            dbgCC = "BITMAP";
17 andreas 4862
            rwt = std::min(wt - border * 2, width);
4863
            rht = std::min(ht - border_size * 2, height);
8 andreas 4864
        break;
4865
 
4866
        case SC_TEXT:
4867
            code = act_sr.jt;
4868
            ix = act_sr.tx;
4869
            iy = act_sr.ty;
10 andreas 4870
            dbgCC = "TEXT";
4871
            border += 4;
69 andreas 4872
            rwt = std::min(wt - border * 2, width);         // We've always a minimum (invisible) border of 4 pixels.
4873
            rht = std::min(ht - border_size * 2, height);   // The height is calculated from a defined border, if any.
8 andreas 4874
        break;
3 andreas 4875
    }
4 andreas 4876
 
17 andreas 4877
    if (width > rwt || height > rht)
4878
        position.overflow = true;
46 andreas 4879
 
4 andreas 4880
    switch (code)
4881
    {
4882
        case 0: // absolute position
8 andreas 4883
            position.left = ix;
69 andreas 4884
            position.top = iy;
40 andreas 4885
 
46 andreas 4886
            if (cc == SC_BITMAP && ix < 0 && rwt < width)
4887
                position.left *= -1;
4888
 
4889
            if (cc == SC_BITMAP && iy < 0 && rht < height)
4890
                position.top += -1;
4891
 
10 andreas 4892
            position.width = rwt;
4893
            position.height = rht;
4 andreas 4894
        break;
4895
 
4896
        case 1: // top, left
10 andreas 4897
            if (cc == SC_TEXT)
40 andreas 4898
            {
10 andreas 4899
                position.left = border;
96 andreas 4900
                position.top = ht - ((ht - rht) / 2) - height * ln;
40 andreas 4901
            }
10 andreas 4902
 
4903
            position.width = rwt;
4904
            position.height = rht;
4 andreas 4905
        break;
4906
 
4907
        case 2: // center, top
40 andreas 4908
            if (cc == SC_TEXT)
96 andreas 4909
                position.top = ht - ((ht - rht) / 2) - height * ln;
40 andreas 4910
 
10 andreas 4911
            position.left = (wt - rwt) / 2;
4912
            position.height = rht;
4913
            position.width = rwt;
4 andreas 4914
        break;
4915
 
4916
        case 3: // right, top
10 andreas 4917
            position.left = wt - rwt;
19 andreas 4918
 
4919
            if (cc == SC_TEXT)
40 andreas 4920
            {
19 andreas 4921
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
96 andreas 4922
                position.top = ht - (ht - rht) - height * ln;
40 andreas 4923
            }
19 andreas 4924
 
10 andreas 4925
            position.width = rwt;
4926
            position.height = rht;
4 andreas 4927
        break;
4928
 
4929
        case 4: // left, middle
10 andreas 4930
            if (cc == SC_TEXT)
40 andreas 4931
            {
10 andreas 4932
                position.left = border;
69 andreas 4933
                position.top = (ht - height) / 2;
40 andreas 4934
            }
4935
            else
4936
                position.top = (ht - rht) / 2;
10 andreas 4937
 
4938
            position.width = rwt;
4939
            position.height = rht;
4 andreas 4940
        break;
4941
 
4942
        case 6: // right, middle
10 andreas 4943
            position.left = wt - rwt;
19 andreas 4944
 
4945
            if (cc == SC_TEXT)
40 andreas 4946
            {
19 andreas 4947
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
69 andreas 4948
                position.top = (ht - height) / 2;
40 andreas 4949
            }
4950
            else
4951
                position.top = (ht - rht) / 2;
19 andreas 4952
 
10 andreas 4953
            position.width = rwt;
4954
            position.height = rht;
4 andreas 4955
        break;
4956
 
4957
        case 7: // left, bottom
10 andreas 4958
            if (cc == SC_TEXT)
40 andreas 4959
            {
10 andreas 4960
                position.left = border_size;
69 andreas 4961
                position.top = (ht - rht) - height * ln;
40 andreas 4962
            }
4963
            else
4964
                position.top = ht - rht;
10 andreas 4965
 
4966
            position.width = rwt;
4967
            position.height = rht;
4 andreas 4968
        break;
4969
 
4970
        case 8: // center, bottom
10 andreas 4971
            position.left = (wt - rwt) / 2;
40 andreas 4972
 
4973
            if (cc == SC_TEXT)
69 andreas 4974
                position.top = (ht - rht) - height * ln;
40 andreas 4975
            else
4976
                position.top = ht - rht;
4977
 
10 andreas 4978
            position.width = rwt;
4979
            position.height = rht;
4 andreas 4980
        break;
4981
 
4982
        case 9: // right, bottom
10 andreas 4983
            position.left = wt - rwt;
19 andreas 4984
 
4985
            if (cc == SC_TEXT)
40 andreas 4986
            {
19 andreas 4987
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
69 andreas 4988
                position.top = (ht - rht) - height * ln;
40 andreas 4989
            }
4990
            else
4991
                position.top = ht - rht;
4 andreas 4992
        break;
4993
 
4994
        default: // center, middle
10 andreas 4995
            position.left = (wt - rwt) / 2;
40 andreas 4996
 
4997
            if (cc == SC_TEXT)
69 andreas 4998
                position.top = (ht - height) / 2;
40 andreas 4999
            else
5000
                position.top = (ht - rht) / 2;
5001
 
10 andreas 5002
            position.width = rwt;
5003
            position.height = rht;
4 andreas 5004
    }
5005
 
69 andreas 5006
    if (TStreamError::checkFilter(HLOG_DEBUG))
5007
    {
70 andreas 5008
        string format = getFormatString((TEXT_ORIENTATION)code);
5009
        MSG_DEBUG("Type: " << dbgCC << ", format: " << format <<
69 andreas 5010
            ", PosType=" << code << ", total height=" << ht << ", height object=" << height <<
5011
            ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width <<
5012
            ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
5013
    }
5014
 
4 andreas 5015
    position.valid = true;
5016
    return position;
5017
}
5018
 
99 andreas 5019
IMAGE_SIZE_t TButton::calcImageSize(int imWidth, int imHeight, int instance, bool aspect)
5020
{
5021
    DECL_TRACER("TButton::calcImageSize(int imWidth, int imHeight, bool aspect)");
5022
 
5023
    int border = getBorderSize(sr[instance].bs);
5024
    IMAGE_SIZE_t isize;
5025
 
5026
    if (!aspect)
5027
    {
5028
        isize.width = wt - border * 2;
5029
        isize.height = ht - border * 2;
5030
    }
5031
    else
5032
    {
5033
        int w = wt - border * 2;
5034
        int h = ht - border * 2;
5035
        double scale;
5036
 
100 andreas 5037
        if (w < h || imWidth > imHeight)
99 andreas 5038
            scale = (double)w / (double)imWidth;
5039
        else
5040
            scale = (double)h / (double)imHeight;
5041
 
5042
        isize.width = (int)((double)imWidth * scale);
5043
        isize.height = (int)((double)imHeight * scale);
5044
    }
5045
 
5046
    MSG_DEBUG("Sizing image: Original: " << imWidth << " x " << imHeight << " to " << isize.width << " x " << isize.height);
5047
    return isize;
5048
}
5049
 
69 andreas 5050
string TButton::getFormatString(TEXT_ORIENTATION to)
5051
{
5052
    DECL_TRACER("TButton::getFormatString(CENTER_CODE cc)");
5053
 
5054
    switch(to)
5055
    {
5056
        case ORI_ABSOLUT:       return "ABSOLUT";
5057
        case ORI_BOTTOM_LEFT:   return "BOTTOM/LEFT";
5058
        case ORI_BOTTOM_MIDDLE: return "BOTTOM/MIDDLE";
5059
        case ORI_BOTTOM_RIGHT:  return "BOTTOM/RIGHT";
5060
        case ORI_CENTER_LEFT:   return "CENTER/LEFT";
5061
        case ORI_CENTER_MIDDLE: return "CENTER/MIDDLE";
5062
        case ORI_CENTER_RIGHT:  return "CENTER/RIGHT";
5063
        case ORI_TOP_LEFT:      return "TOP/LEFT";
5064
        case ORI_TOP_MIDDLE:    return "TOP/MIDDLE";
5065
        case ORI_TOP_RIGHT:     return "TOP/RIGHT";
5066
    }
5067
 
5068
    return "UNKNOWN";   // Should not happen!
5069
}
5070
 
4 andreas 5071
int TButton::getBorderSize(const std::string& name)
5072
{
5073
    DECL_TRACER("TButton::getBorderSize(const std::string& name)");
5074
 
81 andreas 5075
    if (gPageManager && gPageManager->getSystemDraw())
5076
    {
5077
        if (gPageManager->getSystemDraw()->existBorder(name))
5078
            return gPageManager->getSystemDraw()->getBorderWidth(name);
5079
    }
5080
 
4 andreas 5081
    for (int i = 0; sysBorders[i].name != nullptr; i++)
5082
    {
5083
        if (name.compare(sysBorders[i].name) == 0)
8 andreas 5084
        {
5085
            MSG_DEBUG("Border size: " << sysBorders[i].width);
4 andreas 5086
            return sysBorders[i].width;
8 andreas 5087
        }
4 andreas 5088
    }
5089
 
8 andreas 5090
    MSG_DEBUG("Border size: 0");
4 andreas 5091
    return 0;
5092
}
5093
 
5094
void TButton::calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)
5095
{
5096
    DECL_TRACER("TButton::clacImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)");
5097
 
5098
    int spX = btWidth - (btFrame * 2);
5099
    int spY = btHeight - (btFrame * 2);
5100
 
5101
    if (imWidth <= spX && imHeight <= spY)
5102
    {
5103
        *realX = imWidth;
5104
        *realY = imHeight;
5105
        return;
5106
    }
5107
 
5108
    int oversizeX = 0, oversizeY = 0;
5109
 
5110
    if (imWidth > spX)
5111
        oversizeX = imWidth - spX;
5112
 
5113
    if (imHeight > spY)
5114
        oversizeY = imHeight - spY;
5115
 
5116
    double percent = 0.0;
5117
 
5118
    if (oversizeX > oversizeY)
5119
        percent = 100.0 / (double)imWidth * (double)spX;
3 andreas 5120
    else
4 andreas 5121
        percent = 100.0 / (double)imHeight * (double)spY;
5122
 
5123
    *realX = (int)(percent / 100.0 * (double)imWidth);
5124
    *realY = (int)(percent / 100.0 * (double)imHeight);
5125
}
5126
 
10 andreas 5127
SkBitmap TButton::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
4 andreas 5128
{
6 andreas 5129
    DECL_TRACER("TButton::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
4 andreas 5130
 
35 andreas 5131
    if (width <= 0 || height <= 0)
5132
    {
5133
        MSG_WARNING("Got invalid width of height! (width: " << width << ", height: " << height);
5134
        return SkBitmap();
5135
    }
5136
 
6 andreas 5137
    SkPixmap pixmapRed = imgRed.pixmap();
7 andreas 5138
    SkPixmap pixmapMask;
4 andreas 5139
 
7 andreas 5140
    if (!imgMask.empty())
5141
        pixmapMask = imgMask.pixmap();
4 andreas 5142
 
7 andreas 5143
    SkBitmap maskBm;
5144
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
69 andreas 5145
    maskBm.eraseColor(SK_ColorTRANSPARENT);
3 andreas 5146
 
4 andreas 5147
    for (int ix = 0; ix < width; ix++)
5148
    {
5149
        for (int iy = 0; iy < height; iy++)
3 andreas 5150
        {
4 andreas 5151
            SkColor pixelRed = pixmapRed.getColor(ix, iy);
7 andreas 5152
            SkColor pixelMask;
3 andreas 5153
 
7 andreas 5154
            if (!imgMask.empty())
5155
                pixelMask = pixmapMask.getColor(ix, iy);
5156
            else
69 andreas 5157
                pixelMask = SK_ColorTRANSPARENT;
3 andreas 5158
 
10 andreas 5159
            SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
20 andreas 5160
            uint32_t alpha = SkColorGetA(pixel);
35 andreas 5161
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
5162
 
5163
            if (!wpix)
5164
            {
5165
                MSG_ERROR("No pixel buffer!");
5166
                break;
5167
            }
5168
 
20 andreas 5169
            if (alpha == 0)
5170
                pixel = pixelMask;
35 andreas 5171
            // Skia has a bug and has changed the red and the blue color
5172
            // channel. Therefor we must change this 2 color channels for
5173
            // Linux based OSs here. On Android this is not necessary.
5174
#ifdef __ANDROID__
5175
            *wpix = pixel;
5176
#else   // We've to invert the pixels here to have the correct colors
5177
            uchar red   = SkColorGetR(pixel);   // This is blue in reality
5178
            uchar green = SkColorGetG(pixel);
5179
            uchar blue  = SkColorGetB(pixel);   // This is red in reality
5180
            uchar al    = SkColorGetA(pixel);
5181
            *wpix = SkColorSetARGB(al, blue, green, red);
5182
#endif
3 andreas 5183
        }
5184
    }
5185
 
7 andreas 5186
    return maskBm;
3 andreas 5187
}
6 andreas 5188
 
99 andreas 5189
/**
5190
 * @brief Takes 2 images and combines them to one.
5191
 *
5192
 * The 2 images are a solid base image defining the basic form and an identical
5193
 * image defining the alpha channel.
5194
 *
5195
 * @param base  The base image containing the form as black pixels.
5196
 * @param alpha The image containing just an alpha channel.
5197
 * @param col   The color which should be used instead of a black pixel.
5198
 *
5199
 * @return On success a valid bitmap is returned containing the form.
5200
 * On error an empty bitmap is returned.
5201
 */
5202
SkBitmap TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)
5203
{
5204
    DECL_TRACER("TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)");
5205
 
5206
    int width = base.info().width();
5207
    int height = base.info().height();
5208
    SkBitmap Bm;    // The new bitmap. It will be returned in the end.
5209
 
5210
    if (width != alpha.info().width() || height != alpha.info().height())
5211
    {
5212
        MSG_ERROR("Mask and alpha have different size! [ " << width << " x " << height << " to " << alpha.info().width() << " x " << alpha.info().height());
5213
        return Bm;
5214
    }
5215
 
5216
    Bm.allocN32Pixels(width, height);
5217
    Bm.eraseColor(SK_ColorTRANSPARENT);
5218
 
5219
    for (int ix = 0; ix < width; ix++)
5220
    {
5221
        for (int iy = 0; iy < height; iy++)
5222
        {
5223
            SkColor pixelAlpha = alpha.getColor(ix, iy);
100 andreas 5224
            uint32_t *bpix = Bm.getAddr32(ix, iy);
99 andreas 5225
 
100 andreas 5226
            uchar al    = SkColorGetA(pixelAlpha);
99 andreas 5227
            uchar red   = SkColorGetR(col);
5228
            uchar green = SkColorGetG(col);
5229
            uchar blue  = SkColorGetB(col);
5230
 
100 andreas 5231
            if (pixelAlpha == 0)
5232
                red = green = blue = 0;
99 andreas 5233
 
5234
            // Skia has a bug and has changed the red and the blue color
5235
            // channel. Therefor we must change this 2 color channels for
5236
            // Linux based OSs here. On Android this is not necessary.
5237
#ifdef __ANDROID__
100 andreas 5238
            *bpix = SkColorSetARGB(al, red, green, blue);
99 andreas 5239
#else
100 andreas 5240
            *bpix = SkColorSetARGB(al, blue, green, red);
99 andreas 5241
#endif
5242
        }
5243
    }
5244
 
100 andreas 5245
    SkPaint paint;
5246
    paint.setBlendMode(SkBlendMode::kSrcOver);
5247
    SkCanvas can(Bm);
5248
    can.drawBitmap(base, 0, 0, &paint);
99 andreas 5249
    return Bm;
5250
}
5251
 
81 andreas 5252
bool TButton::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)
80 andreas 5253
{
81 andreas 5254
    DECL_TRACER("TButton::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)");
80 andreas 5255
 
5256
    if (width <= 0 || height <= 0)
5257
    {
5258
        MSG_WARNING("Got invalid width or height! (width: " << width << ", height: " << height);
5259
        return false;
5260
    }
5261
 
5262
    SkPixmap pixmap = img->pixmap();
5263
 
5264
    SkBitmap maskBm;
5265
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
5266
    maskBm.eraseColor(SK_ColorTRANSPARENT);
5267
 
5268
    for (int ix = 0; ix < width; ix++)
5269
    {
5270
        for (int iy = 0; iy < height; iy++)
5271
        {
5272
            SkColor pixel = pixmap.getColor(ix, iy);
5273
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
5274
 
5275
            if (!wpix)
5276
            {
5277
                MSG_ERROR("No pixel buffer!");
5278
                break;
5279
            }
5280
 
81 andreas 5281
            uint32_t alpha = SkColorGetA(pixel);
5282
 
5283
            if (alpha == 0 && !useBG)
5284
                pixel = col;
5285
            else if (alpha == 0)
5286
                pixel = bg;
5287
            else
5288
            {
5289
                uint32_t pred = SkColorGetR(pixel);
5290
                uint32_t pgreen = SkColorGetG(pixel);
5291
                uint32_t pblue = SkColorGetB(pixel);
5292
 
5293
                uint32_t red = SkColorGetR(col);
5294
                uint32_t green = SkColorGetG(col);
5295
                uint32_t blue = SkColorGetB(col);
5296
                uint32_t maxChan = SkColorGetG(SK_ColorWHITE);
5297
 
5298
                red   = ((pred == maxChan) ? pred : red);
5299
                green = ((pgreen == maxChan) ? pgreen : green);
5300
                blue  = ((pblue == maxChan) ? pblue : blue);
5301
 
5302
                pixel = SkColorSetARGB(alpha, red, green, blue);
5303
            }
5304
 
80 andreas 5305
            *wpix = pixel;
5306
        }
5307
    }
5308
 
5309
    *img = maskBm;
5310
    return true;
5311
}
5312
 
99 andreas 5313
bool TButton::retrieveImage(const string& path, SkBitmap* image)
5314
{
5315
    DECL_TRACER("TButton::retrieveImage(const string& path, SkBitmap* image)");
5316
 
5317
    sk_sp<SkData> im;
5318
 
5319
    if (!(im = readImage(path)))
5320
        return false;
5321
 
5322
    DecodeDataToBitmap(im, image);
5323
 
5324
    if (image->empty())
5325
    {
5326
        MSG_WARNING("Could not create the image " << path);
5327
        return false;
5328
    }
5329
 
5330
    return true;
5331
}
5332
 
6 andreas 5333
void TButton::show()
5334
{
5335
    DECL_TRACER("TButton::show()");
5336
 
15 andreas 5337
    visible = true;
5338
    makeElement();
6 andreas 5339
 
15 andreas 5340
    if (isSystemButton() && !mSystemReg)
5341
        registerSystemButton();
5342
}
5343
 
5344
void TButton::showLastButton()
5345
{
5346
    DECL_TRACER("TButton::showLastButton()");
5347
 
5348
    if (mLastImage.empty())
5349
        return;
5350
 
38 andreas 5351
    if (!_displayButton && gPageManager)
5352
        _displayButton = gPageManager->getCallbackDB();
5353
 
21 andreas 5354
    if (!prg_stopped && visible && _displayButton)
6 andreas 5355
    {
15 andreas 5356
        ulong parent = mHandle & 0xffff0000;
5357
        size_t rowBytes = mLastImage.info().minRowBytes();
26 andreas 5358
        int rwidth = wt;
5359
        int rheight = ht;
5360
        int rleft = lt;
5361
        int rtop = tp;
43 andreas 5362
#ifdef _SCALE_SKIA_
26 andreas 5363
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5364
        {
5365
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5366
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5367
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5368
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
5369
        }
43 andreas 5370
#endif
26 andreas 5371
        _displayButton(mHandle, parent, (unsigned char *)mLastImage.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
15 andreas 5372
    }
5373
}
5374
 
5375
void TButton::hide(bool total)
5376
{
5377
    DECL_TRACER("TButton::hide()");
5378
 
101 andreas 5379
//    if (type == MULTISTATE_GENERAL && ar == 1)
5380
//        mAniStop = true;
15 andreas 5381
 
21 andreas 5382
    if (!prg_stopped && total)
15 andreas 5383
    {
26 andreas 5384
        int rwidth = wt;
5385
        int rheight = ht;
5386
        int rleft = lt;
5387
        int rtop = tp;
5388
 
38 andreas 5389
        ulong parent = mHandle & 0xffff0000;
5390
        THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
5391
 
5392
        if (tr && tr->mImageRefresh)
5393
        {
5394
            if (tr->mImageRefresh->isRunning())
5395
                tr->mImageRefresh->stop();
5396
        }
43 andreas 5397
#ifdef _SCALE_SKIA_
26 andreas 5398
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5399
        {
5400
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5401
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5402
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5403
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
5404
        }
43 andreas 5405
#endif
28 andreas 5406
        SkBitmap imgButton;
5407
        imgButton.allocN32Pixels(rwidth, rheight);
5408
        imgButton.eraseColor(SK_ColorTRANSPARENT);
5409
        size_t rowBytes = imgButton.info().minRowBytes();
5410
 
38 andreas 5411
        if (!_displayButton && gPageManager)
5412
            _displayButton = gPageManager->getCallbackDB();
5413
 
5414
        if (_displayButton)
5415
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
15 andreas 5416
    }
5417
 
5418
    visible = false;
5419
}
5420
 
5421
bool TButton::isClickable()
5422
{
5423
    DECL_TRACER("TButton::isClickable()");
5424
 
71 andreas 5425
    if (mEnabled && ((cp != 0 && ch != 0) || (lp != 0 && lv != 0) || !op.empty() || !pushFunc.empty() || isSystemButton()) && hs.compare("passThru") != 0)
15 andreas 5426
        return true;
5427
 
5428
    return false;
5429
}
5430
 
5431
/**
5432
 * Handling of system button "connection state". It consists of 12 states
5433
 * indicating the network status. The states have the following meaning:
5434
 *
5435
 * 0      Diconnected (never was connected before since startup)
5436
 * 1 - 6  Connected (blink may be shown with dark and light green)
5437
 * 7, 8   Disconnected (timeout or loss of connection)
5438
 * 9 - 11 Connection in progress
5439
 */
5440
void TButton::funcNetwork(int state)
5441
{
5442
    mutex_sysdraw.lock();
5443
    DECL_TRACER("TButton::funcNetwork(int state)");
5444
 
5445
    mLastLevel = state;
5446
    mActInstance = state;
5447
 
5448
    if (visible)
5449
        makeElement(state);
5450
 
5451
    mutex_sysdraw.unlock();
5452
}
5453
 
5454
/**
5455
 * Handling the timer event from the controller. This comes usualy every
5456
 * 20th part of a second (1 second / 20)
5457
 */
5458
void TButton::funcTimer(const amx::ANET_BLINK& blink)
5459
{
5460
    mutex_sysdraw.lock();
5461
    DECL_TRACER("TButton::funcTimer(const amx::ANET_BLINK& blink)");
5462
 
5463
    string tm;
5464
    std::stringstream sstr;
5465
 
5466
    switch (ad)
5467
    {
5468
        case 141:   // Standard time
5469
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
5470
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << ":"
5471
                 << std::setw(2) << std::setfill('0') << (int)blink.second;
5472
            mLastBlink = blink;
5473
        break;
5474
 
5475
        case 142:   // Time AM/PM
6 andreas 5476
        {
15 andreas 5477
            int hour = (blink.hour > 12) ? (blink.hour - 12) : blink.hour;
5478
            sstr << std::setw(2) << std::setfill('0') << hour << ":"
5479
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << " ";
5480
 
5481
            if (blink.hour <= 12)
5482
                sstr << "AM";
5483
            else
5484
                sstr << "PM";
5485
 
5486
            mLastBlink = blink;
6 andreas 5487
        }
15 andreas 5488
        break;
6 andreas 5489
 
15 andreas 5490
        case 143:   // Time 24 hours
5491
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
5492
                 << std::setw(2) << std::setfill('0') << (int)blink.minute;
5493
            mLastBlink = blink;
5494
        break;
5495
 
5496
        case 151:   // Weekday
5497
            switch (blink.weekday)
5498
            {
5499
                case 0: sstr << "Monday"; break;
5500
                case 1: sstr << "Tuesday"; break;
5501
                case 2: sstr << "Wednesday"; break;
5502
                case 3: sstr << "Thursday"; break;
5503
                case 4: sstr << "Friday"; break;
5504
                case 5: sstr << "Saturday"; break;
5505
                case 6: sstr << "Sunday"; break;
5506
            }
5507
        break;
5508
 
5509
        case 152:   // Date mm/dd
5510
            sstr << (int)blink.month << "/" << (int)blink.day;
5511
        break;
5512
 
5513
        case 153:   // Date dd/mm
5514
            sstr << (int)blink.day << "/" << (int)blink.month;
5515
        break;
5516
 
5517
        case 154:   // Date mm/dd/yyyy
5518
            sstr << (int)blink.month << "/" << (int)blink.day << "/" << (int)blink.year;
5519
        break;
5520
 
5521
        case 155:   // Date dd/mm/yyyy
5522
            sstr << blink.day << "/" << blink.month << "/" << blink.year;
5523
        break;
5524
 
5525
        case 156:   // Date month dd/yyyy
5526
            switch (blink.month)
5527
            {
5528
                case 1:  sstr << "January"; break;
5529
                case 2:  sstr << "February"; break;
5530
                case 3:  sstr << "March"; break;
5531
                case 4:  sstr << "April"; break;
5532
                case 5:  sstr << "May"; break;
5533
                case 6:  sstr << "June"; break;
5534
                case 7:  sstr << "July"; break;
5535
                case 8:  sstr << "August"; break;
5536
                case 9:  sstr << "September"; break;
5537
                case 10:  sstr << "October"; break;
5538
                case 11:  sstr << "November"; break;
5539
                case 12:  sstr << "December"; break;
5540
            }
5541
 
5542
            sstr << " " << (int)blink.day << "/" << (int)blink.year;
5543
        break;
5544
 
5545
        case 157:   // Date dd month yyyy
5546
            sstr << (int)blink.day;
5547
 
5548
            switch (blink.month)
5549
            {
5550
                case 1:  sstr << "January"; break;
5551
                case 2:  sstr << "February"; break;
5552
                case 3:  sstr << "March"; break;
5553
                case 4:  sstr << "April"; break;
5554
                case 5:  sstr << "May"; break;
5555
                case 6:  sstr << "June"; break;
5556
                case 7:  sstr << "July"; break;
5557
                case 8:  sstr << "August"; break;
5558
                case 9:  sstr << "September"; break;
5559
                case 10:  sstr << "October"; break;
5560
                case 11:  sstr << "November"; break;
5561
                case 12:  sstr << "December"; break;
5562
            }
5563
 
5564
            sstr << " " << (int)blink.year;
5565
        break;
5566
 
5567
        case 158:   // Date yyyy-mm-dd
5568
            sstr << (int)blink.year << "-" << (int)blink.month << "-" << (int)blink.day;
5569
        break;
6 andreas 5570
    }
15 andreas 5571
 
5572
    vector<SR_T>::iterator iter;
5573
    tm = sstr.str();
5574
 
118 andreas 5575
    for (iter = sr.begin(); iter != sr.end(); ++iter)
15 andreas 5576
        iter->te = tm;
5577
 
5578
    if (visible)
5579
        makeElement(mActInstance);
5580
 
5581
    mutex_sysdraw.unlock();
6 andreas 5582
}
7 andreas 5583
 
15 andreas 5584
bool TButton::isPixelTransparent(int x, int y)
5585
{
5586
    DECL_TRACER("TButton::isPixelTransparent(int x, int y)");
5587
 
54 andreas 5588
    // If there is no image we treat it as a non transpararent pixel.
5589
    if (sr[mActInstance].mi.empty() && sr[mActInstance].bm.empty())
5590
        return false;
5591
 
5592
    // The mLastImage must never be empty! Although this should never be true,
5593
    // we treat it as a transparent pixel if it happen.
15 andreas 5594
    if (mLastImage.empty())
5595
    {
5596
        MSG_ERROR("Internal error: No image for button available!");
5597
        return true;
5598
    }
5599
 
54 andreas 5600
    // Make sure the coordinates are inside the bounds. A test for a pixel
5601
    // outside the bounds would lead in an immediate exit because of an assert
5602
    // test in skia. Although this should not happen, we treat the pixel as
5603
    // transparent in this case, because the coordinates didn't hit the button.
31 andreas 5604
    if (x < 0 || x >= mLastImage.info().width() || y < 0 || y >= mLastImage.info().height())
5605
    {
5606
        MSG_ERROR("The X or Y coordinate is out of bounds!");
5607
        MSG_ERROR("X=" << x << ", Y=" << y << ", width=" << mLastImage.info().width() << ", height=" << mLastImage.info().height());
54 andreas 5608
        return true;
31 andreas 5609
    }
5610
 
54 andreas 5611
    float alpha = mLastImage.getAlphaf(x, y);   // Get the alpha value (0.0 to 1.0)
15 andreas 5612
 
5613
    if (alpha != 0.0)
5614
        return false;
5615
 
5616
    return true;
5617
}
5618
 
71 andreas 5619
bool TButton::checkForSound()
5620
{
5621
    DECL_TRACER("TButton::checkForSound()");
5622
 
5623
    vector<SR_T>::iterator iter;
5624
 
5625
    for (iter = sr.begin(); iter != sr.end(); ++iter)
5626
    {
5627
        if (!iter->sd.empty())
5628
            return true;
5629
    }
5630
 
5631
    return false;
5632
}
5633
 
79 andreas 5634
bool TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)
5635
{
5636
    DECL_TRACER("TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)");
5637
 
5638
    if (!bm)
5639
        return false;
5640
 
5641
    if (scaleWidth == 1.0 && scaleHeight == 1.0)
5642
        return true;
5643
 
5644
    SkPaint paint;
5645
    paint.setBlendMode(SkBlendMode::kSrc);
5646
    paint.setFilterQuality(kHigh_SkFilterQuality);
5647
    // Calculate new dimension
5648
    SkImageInfo info = bm->info();
5649
    int width  = std::max(1, (int)((double)info.width() * scaleWidth));
5650
    int height = std::max(1, (int)((double)info.height() * scaleHeight));
80 andreas 5651
    MSG_DEBUG("Scaling image to size " << width << " x " << height);
79 andreas 5652
    // Create a canvas and draw new image
5653
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
5654
    bm->allocN32Pixels(width, height);
5655
    bm->eraseColor(SK_ColorTRANSPARENT);
5656
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5657
    SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
5658
    can.drawImageRect(im, rect, &paint);
5659
    return true;
5660
}
5661
 
5662
bool TButton::stretchImageWidth(SkBitmap *bm, int width)
5663
{
5664
    DECL_TRACER("TButton::stretchImageWidth(SkBitmap *bm, int width)");
5665
 
80 andreas 5666
    if (!bm)
5667
        return false;
5668
 
79 andreas 5669
    SkPaint paint;
5670
    paint.setBlendMode(SkBlendMode::kSrc);
5671
    paint.setFilterQuality(kHigh_SkFilterQuality);
5672
 
5673
    SkImageInfo info = bm->info();
5674
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
80 andreas 5675
    MSG_DEBUG("Width: " << width << ", Height: " << info.height());
79 andreas 5676
    bm->allocN32Pixels(width, info.height());
5677
    bm->eraseColor(SK_ColorTRANSPARENT);
5678
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5679
    SkRect rect = SkRect::MakeXYWH(0, 0, width, info.height());
5680
    can.drawImageRect(im, rect, &paint);
80 andreas 5681
    return true;
79 andreas 5682
}
5683
 
5684
bool TButton::stretchImageHeight(SkBitmap *bm, int height)
5685
{
5686
    DECL_TRACER("TButton::stretchImageHeight(SkBitmap *bm, int height)");
5687
 
80 andreas 5688
    if (!bm)
5689
        return false;
5690
 
79 andreas 5691
    SkPaint paint;
5692
    paint.setBlendMode(SkBlendMode::kSrc);
5693
    paint.setFilterQuality(kHigh_SkFilterQuality);
5694
 
5695
    SkImageInfo info = bm->info();
5696
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
80 andreas 5697
    MSG_DEBUG("Width: " << info.width() << ", Height: " << height);
79 andreas 5698
    bm->allocN32Pixels(info.width(), height);
5699
    bm->eraseColor(SK_ColorTRANSPARENT);
5700
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5701
    SkRect rect = SkRect::MakeXYWH(0, 0, info.width(), height);
5702
    can.drawImageRect(im, rect, &paint);
80 andreas 5703
    return true;
79 andreas 5704
}
5705
 
15 andreas 5706
/**
5707
 * This button got the click because it matches the coordinates of a mouse
5708
 * click. It checkes whether it is clickable or not. If it is clickable, it
5709
 * depends on the type of element what happens.
5710
 */
5711
bool TButton::doClick(int x, int y, bool pressed)
5712
{
11 andreas 5713
    DECL_TRACER("TButton::doClick(bool pressed)");
14 andreas 5714
    amx::ANET_SEND scmd;
15 andreas 5715
    int instance = 0;
31 andreas 5716
    int sx = x, sy = y;
71 andreas 5717
    bool isSystem = isSystemButton();
11 andreas 5718
 
15 andreas 5719
    if (!isClickable())
5720
        return false;
71 andreas 5721
 
5722
    if (gPageManager && !checkForSound() && (ch > 0 || lv > 0 || !pushFunc.empty() || isSystem))
5723
    {
5724
        TSystemSound sysSound(TConfig::getSystemPath(TConfig::SOUNDS));
5725
 
5726
        if (pressed && gPageManager->havePlaySound() && sysSound.getSystemSoundState())
5727
            gPageManager->getCallPlaySound()(sysSound.getTouchFeedbackSound());
5728
    }
5729
 
43 andreas 5730
#ifdef _SCALE_SKIA_
31 andreas 5731
    // To be able to test a button for a transparent pixel, we must scale
5732
    // the coordinates because the test is made on the last drawn image and
5733
    // this image is scaled (if scaling is activated).
5734
    if (TConfig::getScale() && gPageManager && gPageManager->getScaleFactor() != 1.0)
5735
    {
5736
        double scaleFactor = gPageManager->getScaleFactor();
5737
        sx = (int)((double)x * scaleFactor);
5738
        sy = (int)((double)y * scaleFactor);
5739
    }
43 andreas 5740
#endif
11 andreas 5741
    if (type == GENERAL)
5742
    {
71 andreas 5743
        MSG_DEBUG("Button type: GENERAL");
35 andreas 5744
 
71 andreas 5745
        if (isSystem && ch == 17)   // Button sounds on/off
11 andreas 5746
        {
5747
            if (pressed)
71 andreas 5748
            {
5749
                MSG_TRACE("System button sounds are toggled ...");
5750
                bool sstate = TConfig::getSystemSoundState();
5751
 
5752
                if (sstate)
5753
                    mActInstance = instance = 0;
5754
                else
5755
                    mActInstance = instance = 1;
5756
 
5757
                TConfig::saveSystemSoundState(!sstate);
5758
                TConfig::saveSettings();
5759
                drawButton(mActInstance, false);
5760
                showLastButton();
5761
            }
5762
        }
113 andreas 5763
        else if (isSystem && ch == 73)  // Enter setup page
5764
        {
5765
            if (pressed)
5766
            {
5767
                if (gPageManager && gPageManager->haveSetupPage())
5768
                    gPageManager->callSetupPage();
5769
            }
5770
        }
5771
        else if (isSystem && ch == 80)  // Shutdown program
5772
        {
5773
            if (pressed)
5774
            {
5775
                if (gPageManager && gPageManager->haveShutdown())
5776
                    gPageManager->callShutdown();
5777
            }
5778
        }
71 andreas 5779
        else if (fb == FB_MOMENTARY)
5780
        {
5781
            if (pressed)
15 andreas 5782
                instance = 1;
14 andreas 5783
            else
15 andreas 5784
                instance = 0;
5785
 
71 andreas 5786
            MSG_DEBUG("Flavor FB_MOMENTARY, instance=" << instance);
15 andreas 5787
            mActInstance = instance;
5788
            // we ignore the state of the method, because it doesn't matter
5789
            // for the message to the controller.
5790
            drawButton(instance, false);
5791
            // If there is nothing in "hs", then it depends on the pixel of the
5792
            // layer. Only if the pixel the coordinates point to are not
5793
            // transparent, the button takes the click.
31 andreas 5794
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 5795
                return false;
5796
 
79 andreas 5797
            // Play sound, if one is defined
90 andreas 5798
            if (gPageManager)
5799
            {
5800
                if (pressed && gPageManager->havePlaySound() && !sr[0].sd.empty())
5801
                    gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[0].sd);
5802
                else if (!pressed && gPageManager->havePlaySound() && !sr[1].sd.empty())
5803
                    gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[1].sd);
5804
            }
79 andreas 5805
 
15 andreas 5806
            if (pushFunc.empty())   // Don't draw the button if it has a push function defined
5807
                showLastButton();
5808
            else
14 andreas 5809
                mActInstance = 0;
15 andreas 5810
        }
5811
        else if (fb == FB_CHANNEL || fb == FB_NONE)
5812
        {
5813
            if (pressed)
5814
                instance = 1;
5815
            else
5816
                instance = 0;
14 andreas 5817
 
71 andreas 5818
            MSG_DEBUG("Flavor FB_CHANNEL, instance=" << instance);
15 andreas 5819
            // we ignore the state of the method, because it doesn't matter
5820
            // for the message to the controller.
5821
            drawButton(instance, false);
5822
            // If there is nothing in "hs", then it depends on the pixel of the
5823
            // layer. Only if the pixel the coordinates point to are not
5824
            // transparent, the button takes the click.
31 andreas 5825
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 5826
                return false;
14 andreas 5827
        }
15 andreas 5828
        else if (fb == FB_INV_CHANNEL)
5829
        {
5830
            if (pressed)
5831
                instance = 0;
5832
            else
5833
                instance = 1;
14 andreas 5834
 
71 andreas 5835
            MSG_DEBUG("Flavor FB_INV_CHANNEL, instance=" << instance);
15 andreas 5836
            // we ignore the state of the method, because it doesn't matter
5837
            // for the message to the controller.
5838
            drawButton(instance, false);
5839
            // If there is nothing in "hs", then it depends on the pixel of the
5840
            // layer. Only if the pixel the coordinates point to are not
5841
            // transparent, the button takes the click.
31 andreas 5842
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 5843
                return false;
79 andreas 5844
 
5845
            // Play sound, if one is defined
90 andreas 5846
            if (gPageManager)
5847
            {
5848
                if (pressed && gPageManager->havePlaySound() && !sr[1].sd.empty())
5849
                    gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[1].sd);
5850
                else if (!pressed && gPageManager->havePlaySound() && !sr[0].sd.empty())
5851
                    gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[0].sd);
5852
            }
15 andreas 5853
        }
5854
        else if (fb == FB_ALWAYS_ON)
14 andreas 5855
        {
15 andreas 5856
            instance = 1;
5857
            mActInstance = 1;
71 andreas 5858
            MSG_DEBUG("Flavor FB_ALWAYS_ON, instance=" << instance);
15 andreas 5859
            // we ignore the state of the method, because it doesn't matter
5860
            // for the message to the controller.
5861
            drawButton(instance, false);
5862
            // If there is nothing in "hs", then it depends on the pixel of the
5863
            // layer. Only if the pixel the coordinates point to are not
5864
            // transparent, the button takes the click.
31 andreas 5865
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 5866
                return false;
5867
 
79 andreas 5868
            // Play sound, if one is defined
90 andreas 5869
            if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[1].sd.empty())
79 andreas 5870
                gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[1].sd);
71 andreas 5871
        }
5872
 
15 andreas 5873
        if ((cp && ch) || !op.empty())
5874
        {
14 andreas 5875
            scmd.device = TConfig::getChannel();
5876
            scmd.port = cp;
5877
            scmd.channel = ch;
5878
 
15 andreas 5879
            if (op.empty())
5880
            {
5881
                if (instance)
5882
                    scmd.MC = 0x0084;
5883
                else
5884
                    scmd.MC = 0x0085;
5885
            }
14 andreas 5886
            else
15 andreas 5887
            {
5888
                scmd.MC = 0x008b;
5889
                scmd.msg = op;
5890
            }
14 andreas 5891
 
5892
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
5893
            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") << ")");
5894
 
5895
            if (gAmxNet)
15 andreas 5896
            {
5897
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
5898
                    gAmxNet->sendCommand(scmd);
5899
            }
14 andreas 5900
            else
5901
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
5902
        }
5903
    }
15 andreas 5904
    else if (type == MULTISTATE_GENERAL)
5905
    {
79 andreas 5906
        // Play sound, if one is defined
90 andreas 5907
        if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[mActInstance].sd.empty())
79 andreas 5908
            gPageManager->getCallPlaySound()(TConfig::getConfigPath() + "/sounds/" + sr[mActInstance].sd);
5909
 
15 andreas 5910
        if ((cp && ch) || !op.empty())
5911
        {
5912
            scmd.device = TConfig::getChannel();
5913
            scmd.port = cp;
5914
            scmd.channel = ch;
14 andreas 5915
 
15 andreas 5916
            if (op.empty())
5917
            {
5918
                if (pressed || fb == FB_ALWAYS_ON)
5919
                    scmd.MC = 0x0084;
5920
                else
5921
                    scmd.MC = 0x0085;
5922
            }
5923
            else
5924
            {
5925
                scmd.MC = 0x008b;
5926
                scmd.msg = op;
5927
            }
5928
 
5929
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
5930
            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") << ")");
5931
 
5932
            if (gAmxNet)
5933
            {
5934
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
5935
                    gAmxNet->sendCommand(scmd);
5936
            }
5937
            else
5938
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
5939
        }
5940
    }
49 andreas 5941
    else if (type == BARGRAPH && lf.find("active") != string::npos)
5942
    {
5943
        // Find the click position
5944
        int level = 0;
15 andreas 5945
 
49 andreas 5946
        if (dr.compare("horizontal") == 0)
5947
        {
5948
            level = (ri ? (wt - x) : x);
5949
            level = (int)((double)(rh - rl) / (double)wt * (double)level);
5950
        }
5951
        else
5952
        {
5953
            level = (ri ? y : (ht - y));
5954
            level = (int)((double)(rh - rl) / (double)ht * (double)level);
5955
        }
5956
 
5957
        // Draw the bargraph
5958
        if (!drawBargraph(mActInstance, level, visible))
5959
            return false;
5960
 
5961
        // Handle click
5962
        if ((cp && ch) || !op.empty())
5963
        {
5964
            scmd.device = TConfig::getChannel();
5965
            scmd.port = cp;
5966
            scmd.channel = ch;
5967
 
5968
            if (op.empty())
5969
            {
5970
                if (pressed || fb == FB_ALWAYS_ON)
5971
                    scmd.MC = 0x0084;
5972
                else
5973
                    scmd.MC = 0x0085;
5974
            }
5975
            else
5976
            {
5977
                scmd.MC = 0x008b;
5978
                scmd.msg = op;
5979
            }
5980
 
5981
            if (gAmxNet)
5982
            {
5983
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
5984
                    gAmxNet->sendCommand(scmd);
5985
            }
5986
            else
5987
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
5988
        }
5989
 
5990
        // Send the level
5991
        if (lp && lv)
5992
        {
5993
            scmd.device = TConfig::getChannel();
5994
            scmd.port = lp;
5995
            scmd.channel = lv;
5996
            scmd.level = lv;
5997
            scmd.value = level;
5998
            scmd.MC = 0x008a;
5999
 
6000
            if (gAmxNet)
6001
                gAmxNet->sendCommand(scmd);
6002
        }
6003
    }
50 andreas 6004
    else if (type == TEXT_INPUT)
6005
    {
6006
        MSG_DEBUG("Text area detected. Switching on keyboard");
6007
        // Drawing background graphic (visible part of area)
6008
        drawTextArea(mActInstance);
6009
    }
49 andreas 6010
 
15 andreas 6011
    /* FIXME: Move the following to class TPageManager!
6012
     *        To do that, the preconditions are to be implemented. It must be
6013
     *        possible to find the button and get access to the credentials
6014
     *        of it.
6015
     */
14 andreas 6016
    if (!pushFunc.empty() && pressed)
6017
    {
53 andreas 6018
        MSG_DEBUG("Executing a push function ...");
14 andreas 6019
        vector<PUSH_FUNC_T>::iterator iter;
6020
 
118 andreas 6021
        for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
14 andreas 6022
        {
53 andreas 6023
            MSG_DEBUG("Testing for function " << iter->pfType);
6024
 
15 andreas 6025
            if (iter->pfType == "sShow")            // show popup
11 andreas 6026
            {
14 andreas 6027
                if (gPageManager)
6028
                    gPageManager->showSubPage(iter->pfName);
11 andreas 6029
            }
15 andreas 6030
            else if (iter->pfType == "sHide")       // hide popup
11 andreas 6031
            {
14 andreas 6032
                if (gPageManager)
6033
                    gPageManager->hideSubPage(iter->pfName);
11 andreas 6034
            }
15 andreas 6035
            else if (iter->pfType == "scGroup")     // hide group
14 andreas 6036
            {
6037
                if (gPageManager)
6038
                    gPageManager->closeGroup(iter->pfName);
6039
            }
15 andreas 6040
            else if (iter->pfType == "Stan")        // Flip to standard page
14 andreas 6041
            {
6042
                if (gPageManager)
6043
                {
15 andreas 6044
                    TPage *page = gPageManager->getActualPage();
6045
 
6046
                    if (!page)
6047
                    {
6048
                        MSG_DEBUG("Internal error: No actual page found!");
6049
                        return false;
6050
                    }
6051
 
6052
                    TSettings *settings = gPageManager->getSettings();
6053
 
6054
                    if (settings->getPowerUpPage().compare(page->getName()) != 0)
6055
                        gPageManager->setPage(settings->getPowerUpPage());
6056
                }
6057
            }
6058
            else if (iter->pfType == "Prev")        // Flip to previous page
6059
            {
6060
                if (gPageManager)
6061
                {
6062
                    int old = gPageManager->getPreviousPageNumber();
6063
 
6064
                    if (old > 0)
6065
                        gPageManager->setPage(old);
6066
                }
6067
            }
6068
            else if (iter->pfType == "sToggle")     // Toggle popup state
6069
            {
6070
                if (gPageManager)
6071
                {
14 andreas 6072
                    TSubPage *page = gPageManager->getSubPage(iter->pfName);
11 andreas 6073
 
14 andreas 6074
                    if (page && page->isVisible())
6075
                        gPageManager->hideSubPage(iter->pfName);
6076
                    else if (page)
6077
                        gPageManager->showSubPage(iter->pfName);
6078
                }
6079
            }
11 andreas 6080
        }
6081
    }
15 andreas 6082
 
6083
    return true;
11 andreas 6084
}
6085
 
7 andreas 6086
/**
6087
 * Based on the pixels in the \a basePix, the function decides whether to return
6088
 * the value of \a col1 or \a col2. A red pixel returns the color \a col1 and
6089
 * a green pixel returns the color \a col2. If there is no red and no green
6090
 * pixel, a transparent pixel is returned.
6091
 *
6092
 * @param basePix
6093
 * This is a pixel from a mask containing red and/or green pixels.
6094
 *
6095
 * @param maskPix
6096
 * This is a pixel from a mask containing more or less tranparent pixels. If
6097
 * the alpha channel of \a basePix is 0 (transparent) this pixel is returned.
6098
 *
6099
 * @param col1
6100
 * The first color.
6101
 *
6102
 * @param col2
6103
 * The second color.
6104
 *
6105
 * @return
6106
 * An array containing the color for one pixel.
6107
 */
10 andreas 6108
SkColor TButton::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
7 andreas 6109
{
6110
    uint alpha = SkColorGetA(basePix);
23 andreas 6111
    uint green = SkColorGetG(basePix);
6112
#ifndef __ANDROID__
7 andreas 6113
    uint red = SkColorGetR(basePix);
23 andreas 6114
#else
6115
    uint red = SkColorGetB(basePix);
6116
#endif
7 andreas 6117
 
6118
    if (alpha == 0)
6119
        return maskPix;
6120
 
6121
    if (red && green)
6122
    {
69 andreas 6123
        if (red > green)
6124
            return col1;
6125
        else
6126
            return col2;
7 andreas 6127
    }
6128
 
6129
    if (red)
6130
        return col1;
6131
 
6132
    if (green)
6133
        return col2;
6134
 
6135
    return SK_ColorTRANSPARENT; // transparent pixel
6136
}
6137
 
6138
TEXT_EFFECT TButton::textEffect(const std::string& effect)
6139
{
6140
    DECL_TRACER("TButton::textEffect(const std::string& effect)");
6141
 
6142
    if (effect == "Outline-S")
6143
        return EFFECT_OUTLINE_S;
6144
    else if (effect == "Outline-M")
6145
        return EFFECT_OUTLINE_M;
6146
    else if (effect == "Outline-L")
6147
        return EFFECT_OUTLINE_L;
6148
    else if (effect == "Outline-X")
6149
        return EFFECT_OUTLINE_X;
6150
    else if (effect == "Glow-S")
6151
        return EFFECT_GLOW_S;
6152
    else if (effect == "Glow-M")
6153
        return EFFECT_GLOW_M;
6154
    else if (effect == "Glow-L")
6155
        return EFFECT_GLOW_L;
6156
    else if (effect == "Glow-X")
6157
        return EFFECT_GLOW_X;
6158
    else if (effect == "Soft Drop Shadow 1")
6159
        return EFFECT_SOFT_DROP_SHADOW_1;
6160
    else if (effect == "Soft Drop Shadow 2")
6161
        return EFFECT_SOFT_DROP_SHADOW_2;
6162
    else if (effect == "Soft Drop Shadow 3")
6163
        return EFFECT_SOFT_DROP_SHADOW_3;
6164
    else if (effect == "Soft Drop Shadow 4")
6165
        return EFFECT_SOFT_DROP_SHADOW_4;
6166
    else if (effect == "Soft Drop Shadow 5")
6167
        return EFFECT_SOFT_DROP_SHADOW_5;
6168
    else if (effect == "Soft Drop Shadow 6")
6169
        return EFFECT_SOFT_DROP_SHADOW_6;
6170
    else if (effect == "Soft Drop Shadow 7")
6171
        return EFFECT_SOFT_DROP_SHADOW_7;
6172
    else if (effect == "Soft Drop Shadow 8")
6173
        return EFFECT_SOFT_DROP_SHADOW_8;
6174
    else if (effect == "Medium Drop Shadow 1")
6175
        return EFFECT_MEDIUM_DROP_SHADOW_1;
6176
    else if (effect == "Medium Drop Shadow 2")
6177
        return EFFECT_MEDIUM_DROP_SHADOW_2;
6178
    else if (effect == "Medium Drop Shadow 3")
6179
        return EFFECT_MEDIUM_DROP_SHADOW_3;
6180
    else if (effect == "Medium Drop Shadow 4")
6181
        return EFFECT_MEDIUM_DROP_SHADOW_4;
6182
    else if (effect == "Medium Drop Shadow 5")
6183
        return EFFECT_MEDIUM_DROP_SHADOW_5;
6184
    else if (effect == "Medium Drop Shadow 6")
6185
        return EFFECT_MEDIUM_DROP_SHADOW_6;
6186
    else if (effect == "Medium Drop Shadow 7")
6187
        return EFFECT_MEDIUM_DROP_SHADOW_7;
6188
    else if (effect == "Medium Drop Shadow 8")
6189
        return EFFECT_MEDIUM_DROP_SHADOW_8;
6190
    else if (effect == "Hard Drop Shadow 1")
6191
        return EFFECT_HARD_DROP_SHADOW_1;
6192
    else if (effect == "Hard Drop Shadow 2")
6193
        return EFFECT_HARD_DROP_SHADOW_2;
6194
    else if (effect == "Hard Drop Shadow 3")
6195
        return EFFECT_HARD_DROP_SHADOW_3;
6196
    else if (effect == "Hard Drop Shadow 4")
6197
        return EFFECT_HARD_DROP_SHADOW_4;
6198
    else if (effect == "Hard Drop Shadow 5")
6199
        return EFFECT_HARD_DROP_SHADOW_5;
6200
    else if (effect == "Hard Drop Shadow 6")
6201
        return EFFECT_HARD_DROP_SHADOW_6;
6202
    else if (effect == "Hard Drop Shadow 7")
6203
        return EFFECT_HARD_DROP_SHADOW_7;
6204
    else if (effect == "Hard Drop Shadow 8")
6205
        return EFFECT_HARD_DROP_SHADOW_8;
6206
    else if (effect == "Soft Drop Shadow 1 with outline")
6207
        return EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE;
6208
    else if (effect == "Soft Drop Shadow 2 with outline")
6209
        return EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE;
6210
    else if (effect == "Soft Drop Shadow 3 with outline")
6211
        return EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE;
6212
    else if (effect == "Soft Drop Shadow 4 with outline")
6213
        return EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE;
6214
    else if (effect == "Soft Drop Shadow 5 with outline")
6215
        return EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE;
6216
    else if (effect == "Soft Drop Shadow 6 with outline")
6217
        return EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE;
6218
    else if (effect == "Soft Drop Shadow 7 with outline")
6219
        return EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE;
6220
    else if (effect == "Soft Drop Shadow 8 with outline")
6221
        return EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE;
6222
    else if (effect == "Medium Drop Shadow 1 with outline")
6223
        return EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE;
6224
    else if (effect == "Medium Drop Shadow 2 with outline")
6225
        return EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE;
6226
    else if (effect == "Medium Drop Shadow 3 with outline")
6227
        return EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE;
6228
    else if (effect == "Medium Drop Shadow 4 with outline")
6229
        return EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE;
6230
    else if (effect == "Medium Drop Shadow 5 with outline")
6231
        return EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE;
6232
    else if (effect == "Medium Drop Shadow 6 with outline")
6233
        return EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE;
6234
    else if (effect == "Medium Drop Shadow 7 with outline")
6235
        return EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE;
6236
    else if (effect == "Medium Drop Shadow 8 with outline")
6237
        return EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE;
6238
    else if (effect == "Hard Drop Shadow 1 with outline")
6239
        return EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE;
6240
    else if (effect == "Hard Drop Shadow 2 with outline")
6241
        return EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE;
6242
    else if (effect == "Hard Drop Shadow 3 with outline")
6243
        return EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE;
6244
    else if (effect == "Hard Drop Shadow 4 with outline")
6245
        return EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE;
6246
    else if (effect == "Hard Drop Shadow 5 with outline")
6247
        return EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE;
6248
    else if (effect == "Hard Drop Shadow 6 with outline")
6249
        return EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE;
6250
    else if (effect == "Hard Drop Shadow 7 with outline")
6251
        return EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE;
6252
    else if (effect == "Hard Drop Shadow 8 with outline")
6253
        return EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE;
6254
 
6255
    return EFFECT_NONE;
6256
}
15 andreas 6257
 
6258
bool TButton::isSystemButton()
6259
{
6260
    DECL_TRACER("TButton::isSystemButton()");
6261
 
6262
    int i = 0;
6263
 
6264
    while (sysButtons[i].channel)
6265
    {
36 andreas 6266
        if (sysButtons[i].type == MULTISTATE_BARGRAPH && lp == 0 && lv == sysButtons[i].channel)
15 andreas 6267
            return true;
36 andreas 6268
        else if (ap == 0 && ad == sysButtons[i].channel)
6269
            return true;
71 andreas 6270
        else if (cp == 0 && ch == sysButtons[i].channel)
6271
            return true;
15 andreas 6272
 
6273
        i++;
6274
    }
6275
 
6276
    return false;
6277
}
21 andreas 6278
 
6279
THR_REFRESH_t *TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)
6280
{
6281
    DECL_TRACER("TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)");
6282
 
6283
    THR_REFRESH_t *p = mThrRefresh;
6284
    THR_REFRESH_t *r, *last = p;
6285
 
6286
    if (!refr || !handle || !parent || bi <= 0)
6287
    {
6288
        MSG_ERROR("Invalid parameter!");
6289
        return nullptr;
6290
    }
6291
 
6292
    r = new THR_REFRESH_t;
6293
    r->mImageRefresh = refr;
6294
    r->handle = handle;
6295
    r->parent = parent;
6296
    r->bi = bi;
6297
    r->next = nullptr;
6298
 
6299
    // If the chain is empty, add the new item;
6300
    if (!mThrRefresh)
6301
        mThrRefresh = r;
6302
    else    // Find the end and append the item
6303
    {
6304
        while (p)
6305
        {
6306
            last = p;
6307
 
6308
            if (p->handle == handle && p->parent == parent && p->bi == bi)
6309
            {
6310
                MSG_WARNING("Duplicate button found! Didn't add it again.");
6311
                delete r;
6312
                return p;
6313
            }
6314
 
6315
            p = p->next;
6316
        }
6317
 
6318
        last->next = r;
6319
    }
6320
 
6321
    MSG_DEBUG("New dynamic button added.");
6322
    return r;
6323
}
6324
 
6325
THR_REFRESH_t *TButton::_findResource(ulong handle, ulong parent, int bi)
6326
{
6327
    DECL_TRACER("TButton::_findResource(ulong handle, ulong parent, int bi)");
6328
 
6329
    THR_REFRESH_t *p = mThrRefresh;
6330
 
6331
    while (p)
6332
    {
6333
        if (p->handle == handle && p->parent == parent && p->bi == bi)
6334
            return p;
6335
 
6336
        p = p->next;
6337
    }
6338
 
6339
    return nullptr;
6340
}
94 andreas 6341
 
6342
void TButton::addToBitmapCache(BITMAP_CACHE& bc)
6343
{
6344
    DECL_TRACER("TButton::addToBitmapCache(BITMAP_CACHE& bc)");
6345
 
6346
    mutex_bmCache.lock();
6347
 
6348
    if (nBitmapCache.size() == 0)
6349
    {
6350
        nBitmapCache.push_back(bc);
6351
        mutex_bmCache.unlock();
6352
        return;
6353
    }
6354
 
6355
    vector<BITMAP_CACHE>::iterator iter;
6356
 
6357
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6358
    {
6359
        if (iter->handle == bc.handle && iter->parent == bc.parent && iter->bi == bc.bi)
6360
        {
97 andreas 6361
            nBitmapCache.erase(iter);
6362
            nBitmapCache.push_back(bc);
94 andreas 6363
            mutex_bmCache.unlock();
6364
            return;
6365
        }
6366
    }
6367
 
6368
    nBitmapCache.push_back(bc);
6369
    mutex_bmCache.unlock();
6370
}
6371
 
6372
BITMAP_CACHE& TButton::getBCentryByHandle(ulong handle, ulong parent)
6373
{
6374
    DECL_TRACER("TButton::getBCentryByHandle(ulong handle, ulong parent)");
6375
 
6376
    mutex_bmCache.lock();
6377
 
6378
    if (nBitmapCache.size() == 0)
6379
    {
6380
        mutex_bmCache.unlock();
6381
        return mBCDummy;
6382
    }
6383
 
6384
    vector<BITMAP_CACHE>::iterator iter;
6385
 
6386
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6387
    {
6388
        if (iter->handle == handle && iter->parent == parent)
6389
        {
6390
            mutex_bmCache.unlock();
6391
            return *iter;
6392
        }
6393
    }
6394
 
6395
    mutex_bmCache.unlock();
6396
    return mBCDummy;
6397
}
6398
 
6399
BITMAP_CACHE& TButton::getBCentryByBI(int bIdx)
6400
{
6401
    DECL_TRACER("TButton::getBCentryByBI(int bIdx)");
6402
 
6403
    mutex_bmCache.lock();
6404
 
6405
    if (nBitmapCache.size() == 0)
6406
    {
6407
        mutex_bmCache.unlock();
6408
        return mBCDummy;
6409
    }
6410
 
6411
    vector<BITMAP_CACHE>::iterator iter;
6412
 
6413
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6414
    {
6415
        if (iter->bi == bIdx)
6416
        {
6417
            mutex_bmCache.unlock();
6418
            return *iter;
6419
        }
6420
    }
6421
 
6422
    mutex_bmCache.unlock();
6423
    return mBCDummy;
6424
}
6425
 
6426
void TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)
6427
{
6428
    DECL_TRACER("TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)");
6429
 
6430
    if (nBitmapCache.size() == 0 || !elem)
6431
        return;
6432
 
6433
    mutex_bmCache.lock();
6434
    vector<BITMAP_CACHE>::iterator iter;
6435
 
6436
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6437
    {
6438
        if (iter == *elem)
6439
        {
6440
            nBitmapCache.erase(iter);
6441
            mutex_bmCache.unlock();
6442
            return;
6443
        }
6444
    }
6445
 
6446
    mutex_bmCache.unlock();
6447
}
6448
 
6449
void TButton::setReady(ulong handle)
6450
{
6451
    DECL_TRACER("TButton::setReady(ulong handle)");
6452
 
6453
    if (nBitmapCache.size() == 0)
6454
        return;
6455
 
6456
    mutex_bmCache.lock();
6457
    vector<BITMAP_CACHE>::iterator iter;
6458
 
6459
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6460
    {
6461
        if (iter->handle == handle)
6462
        {
6463
            iter->ready = true;
6464
            mutex_bmCache.unlock();
6465
            return;
6466
        }
6467
    }
6468
 
6469
    mutex_bmCache.unlock();
6470
}
6471
 
97 andreas 6472
void TButton::setInvalid(ulong handle)
6473
{
6474
    DECL_TRACER("TButton::setInvalid(ulong handle)");
6475
 
6476
    if (nBitmapCache.size() == 0)
6477
        return;
6478
 
6479
    mutex_bmCache.lock();
6480
    vector<BITMAP_CACHE>::iterator iter;
6481
 
6482
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6483
    {
6484
        if (iter->handle == handle)
6485
        {
6486
            nBitmapCache.erase(iter);
6487
            mutex_bmCache.unlock();
6488
            return;
6489
        }
6490
    }
6491
 
6492
    mutex_bmCache.unlock();
6493
}
6494
 
94 andreas 6495
void TButton::setBCBitmap(ulong handle, SkBitmap& bm)
6496
{
6497
    DECL_TRACER("TButton::setBCBitmap(ulong handle, SkBitmap& bm)");
6498
 
6499
    if (nBitmapCache.size() == 0)
6500
        return;
6501
 
6502
    mutex_bmCache.lock();
6503
    vector<BITMAP_CACHE>::iterator iter;
6504
 
6505
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6506
    {
6507
        if (iter->handle == handle)
6508
        {
6509
            iter->bitmap = bm;
6510
            mutex_bmCache.unlock();
6511
            return;
6512
        }
6513
    }
6514
 
6515
    mutex_bmCache.unlock();
6516
}
6517
 
6518
void TButton::showBitmapCache()
6519
{
6520
    DECL_TRACER("TButton::showBitmapCache()");
6521
 
6522
    mutex_bmCache.lock();
6523
    vector<BITMAP_CACHE>::iterator iter;
6524
    bool found;
6525
 
6526
    while (nBitmapCache.size() > 0)
6527
    {
6528
        found = false;
6529
 
6530
        for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
6531
        {
6532
            if (iter->ready)
6533
            {
6534
                if (_displayButton)
6535
                    _displayButton(iter->handle, iter->parent, (unsigned char *)iter->bitmap.getPixels(), iter->width, iter->height, iter->bitmap.info().minRowBytes(), iter->left, iter->top);
6536
 
6537
                nBitmapCache.erase(iter);
6538
                found = true;
6539
                break;
6540
            }
6541
        }
6542
 
6543
        if (!found)
6544
            break;
6545
    }
6546
 
6547
    mutex_bmCache.unlock();
6548
}