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