Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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