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