Subversion Repositories tpanel

Rev

Rev 481 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
2
 * Copyright (C) 2020 to 2024 by Andreas Theofilu <andreas@theosys.at>
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
 */
18
 
19
#include <string>
20
#include <memory>
21
#include <algorithm>
22
#include <codecvt>
23
#include <fstream>
24
 
25
#include <include/core/SkPixmap.h>
26
#include <include/core/SkSize.h>
27
#include <include/core/SkColor.h>
28
#include <include/core/SkFont.h>
29
#include <include/core/SkTypeface.h>
30
#include <include/core/SkFontMetrics.h>
31
#include <include/core/SkTextBlob.h>
32
#include <include/core/SkRegion.h>
33
#include <include/core/SkImageFilter.h>
34
#include <include/effects/SkImageFilters.h>
35
#include <include/core/SkPath.h>
36
#include <include/core/SkSurfaceProps.h>
37
//#ifndef __MACH__
38
//#include <include/core/SkFilterQuality.h>
39
//#endif
40
#include <include/core/SkMaskFilter.h>
41
//#include <include/core/SkImageEncoder.h>
42
#include <include/core/SkRRect.h>
43
#include <include/core/SkBlurTypes.h>
44
 
45
//#ifdef __ANDROID__
46
//#include <QtAndroidExtras/QAndroidJniObject>
47
//#include <QtAndroid>
48
//#endif
49
 
50
#include "tbutton.h"
51
#include "tbuttonstates.h"
52
#include "thttpclient.h"
53
#include "terror.h"
54
#include "tconfig.h"
55
#include "tresources.h"
56
#include "ticons.h"
57
#include "tamxnet.h"
58
#include "tpagemanager.h"
59
#include "tsystemsound.h"
60
#include "timgcache.h"
61
#include "turl.h"
62
#include "tlock.h"
479 andreas 63
#include "ttpinit.h"
446 andreas 64
#if TESTMODE == 1
65
#include "testmode.h"
66
#endif
67
 
68
#if __cplusplus < 201402L
69
#   error "This module requires at least C++14 standard!"
70
#else
71
#   if __cplusplus < 201703L
72
#       include <experimental/filesystem>
73
namespace fs = std::experimental::filesystem;
74
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
75
#   else
76
#       include <filesystem>
77
#       ifdef __ANDROID__
78
namespace fs = std::__fs::filesystem;
79
#       else
80
namespace fs = std::filesystem;
81
#       endif
82
#   endif
83
#endif
84
 
85
using std::exception;
86
using std::string;
87
using std::vector;
88
using std::unique_ptr;
89
using std::map;
90
using std::pair;
91
using std::min;
92
using std::max;
93
using std::thread;
94
using std::atomic;
95
using std::mutex;
96
using std::bind;
97
using namespace Button;
98
using namespace Expat;
99
 
100
#define MAX_BUFFER          65536
101
 
102
#define RLOG_INFO           0x00fe
103
#define RLOG_WARNING        0x00fd
104
#define RLOG_ERROR          0x00fb
105
#define RLOG_TRACE          0x00f7
106
#define RLOG_DEBUG          0x00ef
107
#define RLOG_PROTOCOL       0x00f8
108
#define RLOG_ALL            0x00e0
109
 
110
extern TIcons *gIcons;
111
extern amx::TAmxNet *gAmxNet;
112
extern TPageManager *gPageManager;
113
 
114
THR_REFRESH_t *TButton::mThrRefresh = nullptr;
115
vector<BITMAP_CACHE> nBitmapCache;     // Holds the images who are delayed because they are external
116
 
117
SYSTEF_t sysTefs[] = {
118
    {  1, "Outline-S" },
119
    {  2, "Outline-M" },
120
    {  3, "Outline-L" },
121
    {  4, "Outline-X" },
122
    {  5, "Glow-S" },
123
    {  6, "Glow-M" },
124
    {  7, "Glow-L" },
125
    {  8, "Glow-X" },
126
    {  9, "Soft Drop Shadow 1" },
127
    { 10, "Soft Drop Shadow 2" },
128
    { 11, "Soft Drop Shadow 3" },
129
    { 12, "Soft Drop Shadow 4" },
130
    { 13, "Soft Drop Shadow 5" },
131
    { 14, "Soft Drop Shadow 6" },
132
    { 15, "Soft Drop Shadow 7" },
133
    { 16, "Soft Drop Shadow 8" },
134
    { 17, "Medium Drop Shadow 1" },
135
    { 18, "Medium Drop Shadow 2" },
136
    { 19, "Medium Drop Shadow 3" },
137
    { 20, "Medium Drop Shadow 4" },
138
    { 21, "Medium Drop Shadow 5" },
139
    { 22, "Medium Drop Shadow 6" },
140
    { 23, "Medium Drop Shadow 7" },
141
    { 24, "Medium Drop Shadow 8" },
142
    { 25, "Hard Drop Shadow 1" },
143
    { 26, "Hard Drop Shadow 2" },
144
    { 27, "Hard Drop Shadow 3" },
145
    { 28, "Hard Drop Shadow 4" },
146
    { 29, "Hard Drop Shadow 5" },
147
    { 30, "Hard Drop Shadow 6" },
148
    { 31, "Hard Drop Shadow 7" },
149
    { 32, "Hard Drop Shadow 8" },
150
    { 33, "Soft Drop Shadow 1 with outline" },
151
    { 34, "Soft Drop Shadow 2 with outline" },
152
    { 35, "Soft Drop Shadow 3 with outline" },
153
    { 36, "Soft Drop Shadow 4 with outline" },
154
    { 37, "Soft Drop Shadow 5 with outline" },
155
    { 38, "Soft Drop Shadow 6 with outline" },
156
    { 39, "Soft Drop Shadow 7 with outline" },
157
    { 40, "Soft Drop Shadow 8 with outline" },
158
    { 41, "Medium Drop Shadow 1 with outline" },
159
    { 42, "Medium Drop Shadow 2 with outline" },
160
    { 43, "Medium Drop Shadow 3 with outline" },
161
    { 44, "Medium Drop Shadow 4 with outline" },
162
    { 45, "Medium Drop Shadow 5 with outline" },
163
    { 46, "Medium Drop Shadow 6 with outline" },
164
    { 47, "Medium Drop Shadow 7 with outline" },
165
    { 48, "Medium Drop Shadow 8 with outline" },
166
    { 49, "Hard Drop Shadow 1 with outline" },
167
    { 50, "Hard Drop Shadow 2 with outline" },
168
    { 51, "Hard Drop Shadow 3 with outline" },
169
    { 52, "Hard Drop Shadow 4 with outline" },
170
    { 53, "Hard Drop Shadow 5 with outline" },
171
    { 54, "Hard Drop Shadow 6 with outline" },
172
    { 55, "Hard Drop Shadow 7 with outline" },
173
    { 56, "Hard Drop Shadow 8 with outline" },
174
    { 0,  "\0" }
175
};
176
 
177
TButton::TButton()
178
{
179
    DECL_TRACER("TButton::TButton()");
180
 
181
    mAniRunning = false;
182
    mLastBlink.clear();
183
}
184
 
185
TButton::~TButton()
186
{
187
    DECL_TRACER("TButton::~TButton()");
188
 
189
    if (ap == 0 && ad == 8)
190
    {
191
        if (gAmxNet)
192
            gAmxNet->deregNetworkState(mHandle);
193
    }
194
 
195
    if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158)))
196
    {
197
        if (gAmxNet)
198
            gAmxNet->deregTimer(mHandle);
199
    }
200
 
201
    if (ap == 0 && ad == 81)    // Network state multi bargraph
202
    {
203
        if (gPageManager)
204
            gPageManager->unregCallbackNetState(mHandle);
205
    }
206
 
207
    if (mTimer)
208
    {
209
        mTimer->stop();
210
 
211
        while (mTimer->isRunning())
212
            usleep(50);
213
 
214
        delete mTimer;
215
    }
216
 
217
    if (mAniRunning)
218
    {
219
        ulong tm = nu * ru + nd * rd;
220
        mAniStop = true;
221
 
222
        while (mAniRunning)
223
            std::this_thread::sleep_for(std::chrono::milliseconds(tm * 100));
224
    }
225
 
226
    THR_REFRESH_t *next, *p = mThrRefresh;
227
 
228
    while (p)
229
    {
230
        if (p->mImageRefresh)
231
        {
232
            p->mImageRefresh->stop();
233
            int counter = 0;
234
 
235
            while (counter < 1000 && p->mImageRefresh->isRunning())
236
            {
237
                usleep(50);
238
                counter++;
239
            }
240
 
241
            delete p->mImageRefresh;
242
            p->mImageRefresh = nullptr;
243
        }
244
 
245
        next = p->next;
246
        delete p;
247
        p = next;
248
    }
249
 
250
    mThrRefresh = nullptr;
251
}
252
 
253
size_t TButton::initialize(TExpat *xml, size_t index)
254
{
255
    DECL_TRACER("TButton::initialize(TExpat *xml, size_t index)");
256
 
257
    if (!xml || index == TExpat::npos)
258
    {
259
        MSG_ERROR("Invalid NULL parameter passed!");
260
        TError::setError();
261
        return TExpat::npos;
262
    }
263
 
264
    mChanged = true;
265
    int lastLevel = 0;
266
    int lastJoyX = 0;
267
    int lastJoyY = 0;
268
    vector<ATTRIBUTE_t> attrs = xml->getAttributes(index);
269
    string stype = xml->getAttribute("type", attrs);
270
    type = getButtonType(stype);
271
    MSG_DEBUG("Button type: " << stype << " --> " << type);
272
    string ename, content;
273
    size_t oldIndex = index;
274
 
275
    while((index = xml->getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
276
    {
277
        if (ename.compare("bi") == 0)
278
        {
279
            bi = xml->convertElementToInt(content);
280
            MSG_DEBUG("Processing button index: " << bi);
281
        }
282
        else if (ename.compare("na") == 0)          // Name
283
            na = content;
284
        else if (ename.compare("bd") == 0)          // Description
285
            bd = content;
286
        else if (ename.compare("lt") == 0)          // Left
287
        {
288
            lt = xml->convertElementToInt(content);
289
            mPosLeft = lt;
290
        }
291
        else if (ename.compare("tp") == 0)          // Top
292
        {
293
            tp = xml->convertElementToInt(content);
294
            mPosTop = tp;
295
        }
296
        else if (ename.compare("wt") == 0)          // Width
297
        {
298
            wt = xml->convertElementToInt(content);
299
            mWidthOrig = wt;
300
        }
301
        else if (ename.compare("ht") == 0)          // Height
302
        {
303
            ht = xml->convertElementToInt(content);
304
            mHeightOrig = ht;
305
        }
306
        else if (ename.compare("zo") == 0)          // Z-Order
307
            zo = xml->convertElementToInt(content);
308
        else if (ename.compare("hs") == 0)          // bounding
309
            hs = content;
310
        else if (ename.compare("bs") == 0)          // border style
311
            bs = content;
312
        else if (ename.compare("fb") == 0)          // feedback type
313
            fb = getButtonFeedback(content);
314
        else if (ename.compare("ap") == 0)          // address port
315
            ap = xml->convertElementToInt(content);
316
        else if (ename.compare("ad") == 0)          // address code
317
            ad = xml->convertElementToInt(content);
318
        else if (ename.compare("ch") == 0)          // channel code
319
            ch = xml->convertElementToInt(content);
320
        else if (ename.compare("cp") == 0)          // channel port
321
            cp = xml->convertElementToInt(content);
322
        else if (ename.compare("lp") == 0)          // level port
323
            lp = xml->convertElementToInt(content);
324
        else if (ename.compare("lv") == 0)          // level code
325
            lv = xml->convertElementToInt(content);
326
        else if (ename.compare("dr") == 0)          // level direction
327
            dr = content;
328
        else if (ename.compare("co") == 0)          // command port
329
            co = xml->convertElementToInt(content);
330
        else if (ename.compare("cm") == 0)          // commands to send on button hit
331
            cm.push_back(content);
332
        else if (ename.compare("va") == 0)          // Level control value
333
            va = xml->convertElementToInt(content);
334
        else if (ename.compare("rm") == 0)          // State count
335
            rm = xml->convertElementToInt(content);
336
        else if (ename.compare("nu") == 0)          // Animate time up
337
            nu = xml->convertElementToInt(content);
338
        else if (ename.compare("nd") == 0)          // Animate time down
339
            nd = xml->convertElementToInt(content);
340
        else if (ename.compare("ar") == 0)          // Auto repeat state
341
            ar = xml->convertElementToInt(content);
342
        else if (ename.compare("ru") == 0)          // Animate time up (bargraph on click)
343
            ru = xml->convertElementToInt(content);
344
        else if (ename.compare("rd") == 0)          // Animate time down (bargraph on click)
345
            rd = xml->convertElementToInt(content);
346
        else if (ename.compare("lu") == 0)          // Animate time up (bargraph)
347
            lu = xml->convertElementToInt(content);
348
        else if (ename.compare("ld") == 0)          // Animate time down (bargraph)
349
            ld = xml->convertElementToInt(content);
350
        else if (ename.compare("rv") == 0)          // Level control repeat time
351
            rv = xml->convertElementToInt(content);
352
        else if (ename.compare("rl") == 0)          // Bargraph range low
353
            rl = xml->convertElementToInt(content);
354
        else if (ename.compare("rh") == 0)          // Bargraph range high
355
            rh = xml->convertElementToInt(content);
356
        else if (ename.compare("ri") == 0)          // Bargraph inverted (0 = normal, 1 = inverted)
357
        {
358
            ri = xml->convertElementToInt(content);
359
 
360
            if (ri > 0 && lf != "center" && lf != "dragCenter")
361
            {
362
                lastLevel = rh - rl;
363
                lastJoyX = lastLevel;
364
            }
365
        }
366
        else if (ename.compare("ji") == 0)          // Joystick aux inverted (0 = normal, 1 = inverted)
367
        {
368
            ji = xml->convertElementToInt(content);
369
 
370
            if (ji > 0 && lf != "center" && lf != "dragCenter")
371
                lastJoyY = rh - rl;
372
        }
373
        else if (ename.compare("rn") == 0)          // Bargraph: Range drag increment
374
            rn = xml->convertElementToInt(content);
375
        else if (ename.compare("lf") == 0)          // Bargraph function
376
            lf = content;
377
        else if (ename.compare("sd") == 0)          // Name/Type of slider for a bargraph
378
            sd = content;
379
        else if (ename.compare("vt") == 0)          // Level control type
380
            vt = content;
381
        else if (ename.compare("cd") == 0)          // Name of cursor for a joystick
382
            cd = content;
383
        else if (ename.compare("sc") == 0)          // Color of slider (for bargraph)
384
            sc = content;
385
        else if (ename.compare("cc") == 0)          // Color of cursor (for joystick)
386
            cc = content;
387
        else if (ename.compare("mt") == 0)          // Length of text area
388
            mt = xml->convertElementToInt(content);
389
        else if (ename.compare("dt") == 0)          // Textarea multiple/single line
390
            dt = content;
391
        else if (ename.compare("im") == 0)          // Input mask of a text area
392
            im = content;
393
        else if (ename.compare("so") == 0)          // String output port
394
            so = xml->convertElementToInt(content);
395
        else if (ename.compare("op") == 0)          // String the button send
396
            op = content;
397
        else if (ename.compare("pc") == 0)          // Password character for text area (single line)
398
            pc = content;
399
        else if (ename.compare("pp") == 0)          // Password protection
400
            pp = xml->convertElementToInt(content);
401
        else if (ename.compare("ta") == 0)          // Listbox table channel
402
            ta = xml->convertElementToInt(content);
403
        else if (ename.compare("ti") == 0)          // Listbox table address channel of rows
404
            ti = xml->convertElementToInt(content);
405
        else if (ename.compare("tr") == 0)          // Listbox number of rows
406
            tr = xml->convertElementToInt(content);
407
        else if (ename.compare("tc") == 0)          // Listbox number of columns
408
            tc = xml->convertElementToInt(content);
409
        else if (ename.compare("tj") == 0)          // Listbox row height
410
            tj = xml->convertElementToInt(content);
411
        else if (ename.compare("tk") == 0)          // Listbox preferred row height
412
            tk = xml->convertElementToInt(content);
413
        else if (ename.compare("of") == 0)          // Listbox list offset: 0=disabled/1=enabled
414
            of = xml->convertElementToInt(content);
415
        else if (ename.compare("tg") == 0)          // Listbox managed: 0=no/1=yes
416
            tg = xml->convertElementToInt(content);
417
        else if (ename.compare("st") == 0)          // SubPageView index
418
            st = xml->convertElementToInt(content);
419
        else if (ename.compare("ws") == 0)          // SubPageView: Wrap subpages; 1 = YES
420
            ws = xml->convertElementToInt(content);
421
        else if (ename.compare("sa") == 0)          // SubPageView: Percent of space between items in list
422
            sa = xml->convertElementToInt(content);
423
        else if (ename.compare("dy") == 0)          // SubPageView: Allow dynamic reordering; 1 = YES
424
            dy = xml->convertElementToInt(content);
425
        else if (ename.compare("rs") == 0)          // SubPageView: Reset view on show; 1 = YES
426
            rs = xml->convertElementToInt(content);
427
        else if (ename.compare("on") == 0)          // SubPageView direction
428
            on = content;
429
        else if (ename.compare("ba") == 0)          // SubPageView scrollbar
430
            ba = xml->convertElementToInt(content);
431
        else if (ename.compare("bo") == 0)          // SubPageView scrollbar offset
432
            bo = xml->convertElementToInt(content);
433
        else if (ename.compare("we") == 0)          // SubViewPage Anchor position
434
            we = content;
435
        else if (ename.compare("hd") == 0)          // 1 = Hidden, 0 = Normal visible
436
            hd = xml->convertElementToInt(content);
437
        else if (ename.compare("da") == 0)          // 1 = Disabled, 0 = Normal active
438
            da = xml->convertElementToInt(content);
439
        else if (ename.compare("ac") == 0)          // Direction of text (guess)
440
        {
441
            ac_di = xml->getAttributeInt("di", attrs);  // 0 = left to right; 1 = right to left
442
        }
480 andreas 443
        else if (ename.compare("pf") == 0)          // Function call TP4
446 andreas 444
        {
445
            PUSH_FUNC_T pf;
446
            pf.pfName = content;
447
            pf.pfType = xml->getAttribute("type", attrs);
448
            pushFunc.push_back(pf);
449
        }
480 andreas 450
        else if (ename.compare("er") == 0)          // Function call TP5
451
        {
452
            PUSH_FUNC_T pf;
453
            string e;
454
 
455
            while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
456
            {
457
                if (e.compare("pgFlip") == 0)
458
                {
459
                    pf.item = xml->getAttributeInt("item", attrs);
460
                    pf.pfType = xml->getAttribute("type", attrs);
461
                    pf.pfName = content;
462
                    pushFunc.push_back(pf);
463
                }
464
            }
465
        }
466
        else if (ename.compare("ep") == 0)          // TP5: Call an application
467
        {
468
            CALL_APP_t ep;
469
            string e;
470
 
471
            while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
472
            {
473
                if (e.compare("launch") == 0)
474
                {
475
                    ep.item = xml->getAttributeInt("item", attrs);
476
                    ep.action = xml->getAttribute("action", attrs);
477
                    ep.id = xml->getAttributeInt("id", attrs);
478
                    ep.name = content;
479
                    callApp.push_back(ep);
480
                }
481
            }
482
        }
446 andreas 483
        else if (ename.compare("sr") == 0)          // Section state resources
484
        {
485
            SR_T bsr;
486
            bsr.number = xml->getAttributeInt("number", attrs); // State number
487
            string e;
488
 
489
            while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
490
            {
491
                if (e.compare("do") == 0)           // Draw order
492
                    bsr._do = content;
493
                else if (e.compare("bs") == 0)      // Frame type
494
                    bsr.bs = content;
495
                else if (e.compare("mi") == 0)      // Chameleon image
496
                    bsr.mi = content;
497
                else if (e.compare("cb") == 0)      // Border color
498
                    bsr.cb = content;
499
                else if (e.compare("cf") == 0)      // Fill color
500
                    bsr.cf = content;
501
                else if (e.compare("ct") == 0)      // Text color
502
                    bsr.ct = content;
503
                else if (e.compare("ec") == 0)      // Text effect color
504
                    bsr.ec = content;
505
                else if (e.compare("bm") == 0)      // Bitmap image
506
                {
507
                    bsr.bm = content;
508
                    bsr.dynamic = ((xml->getAttributeInt("dynamic", attrs) == 1) ? true : false);
509
                }
462 andreas 510
                else if (e.compare("bitmapEntry") == 0) // G5 start of bitmap table
511
                {
512
                    string fname;
479 andreas 513
                    BITMAPS_t bitmapEntry;
462 andreas 514
 
515
                    while ((index = xml->getNextElementFromIndex(index, &fname, &content, &attrs)) != TExpat::npos)
516
                    {
517
                        if (fname.compare("fileName") == 0)
479 andreas 518
                            bitmapEntry.fileName = content;
519
                        else if (fname.compare("justification") == 0)
481 andreas 520
                            bitmapEntry.justification = static_cast<ORIENTATION>(xml->convertElementToInt(content));
479 andreas 521
                        else if (fname.compare("offsetX") == 0)
522
                            bitmapEntry.offsetX = xml->convertElementToInt(content);
523
                        else if (fname.compare("offsetY") == 0)
524
                            bitmapEntry.offsetY = xml->convertElementToInt(content);
462 andreas 525
                    }
479 andreas 526
 
527
                    bsr.bitmaps.push_back(bitmapEntry);
462 andreas 528
                }
446 andreas 529
                else if (e.compare("sd") == 0)      // Sound file
530
                    bsr.sd = content;
531
                else if (e.compare("sb") == 0)      // index external graphic
532
                    bsr.sb = xml->convertElementToInt(content);
533
                else if (e.compare("ii") == 0)      // Icon index
534
                    bsr.ii = xml->convertElementToInt(content);
535
                else if (e.compare("ji") == 0)      // Icon/bitmap orientation
536
                    bsr.ji = xml->convertElementToInt(content);
537
                else if (e.compare("jb") == 0)      // Bitmap orientation
538
                    bsr.jb = xml->convertElementToInt(content);
539
                else if (e.compare("bx") == 0)      // Absolute image position X
540
                    bsr.bx = xml->convertElementToInt(content);
541
                else if (e.compare("by") == 0)      // Absolute image position Y
542
                    bsr.by = xml->convertElementToInt(content);
543
                else if (e.compare("ix") == 0)      // Absolute Icon position X
544
                    bsr.ix = xml->convertElementToInt(content);
545
                else if (e.compare("iy") == 0)      // Absolute Icon position Y
546
                    bsr.iy = xml->convertElementToInt(content);
547
                else if (e.compare("fi") == 0)      // Font index
548
                    bsr.fi = xml->convertElementToInt(content);
549
                else if (e.compare("te") == 0)      // Text
550
                    bsr.te = content;
462 andreas 551
                else if (e.compare("ff") == 0)      // G5 font file name
552
                    bsr.ff = content;
553
                else if (e.compare("fs") == 0)      // G5 font size
554
                    bsr.fs = xml->convertElementToInt(content);
446 andreas 555
                else if (e.compare("jt") == 0)      // Text orientation
479 andreas 556
                    bsr.jt = (ORIENTATION)xml->convertElementToInt(content);
446 andreas 557
                else if (e.compare("tx") == 0)      // Absolute text position X
558
                    bsr.tx = xml->convertElementToInt(content);
559
                else if (e.compare("ty") == 0)      // Absolute text position Y
560
                    bsr.ty = xml->convertElementToInt(content);
561
                else if (e.compare("ww") == 0)      // Word wrap
562
                    bsr.ww = xml->convertElementToInt(content);
563
                else if (e.compare("et") == 0)      // Text effects
564
                    bsr.et = xml->convertElementToInt(content);
565
                else if (e.compare("oo") == 0)      // Opacity
566
                    bsr.oo = xml->convertElementToInt(content);
567
                else if (e.compare("md") == 0)      // Marquee type
568
                    bsr.md = xml->convertElementToInt(content);
569
                else if (e.compare("mr") == 0)      // Marquee enable/disable
570
                    bsr.mr = xml->convertElementToInt(content);
571
 
572
                oldIndex = index;
573
            }
574
 
575
            sr.push_back(bsr);
576
        }
577
 
578
        if (index == TExpat::npos)
579
            index = oldIndex + 1;
580
    }
581
 
582
    visible = !hd;  // set the initial visibility
583
 
584
    if (gPageManager)
585
    {
586
        TButtonStates *pbs = gPageManager->addButtonState(type, ap, ad, ch, cp, lp, lv);
587
 
588
        if (!pbs)
589
        {
590
            MSG_ERROR("States of actual button " << bi << " (" << na << ") are not found!");
591
        }
592
        else
593
        {
594
            mButtonID = pbs->getID();
595
            MSG_DEBUG("Button ID: " << getButtonIDstr() << ", type: " << buttonTypeToString() << ", index: " << bi << ", name: " << na);
596
            pbs->setLastLevel(lastLevel);
597
            pbs->setLastJoyX(lastJoyX);
598
            pbs->setLastJoyY(lastJoyY);
599
        }
600
    }
601
/*
602
    if (sr.size() > 0 && TStreamError::checkFilter(HLOG_DEBUG))
603
    {
604
        MSG_DEBUG("bi  : " << bi);
605
        MSG_DEBUG("na  : " << na);
606
        MSG_DEBUG("type: " << type);
607
        MSG_DEBUG("lt  : " << lt);
608
        MSG_DEBUG("tp  : " << tp);
609
        MSG_DEBUG("wt  : " << wt);
610
        MSG_DEBUG("ht  : " << ht);
611
 
612
        vector<SR_T>::iterator iter;
613
        size_t pos = 1;
614
 
615
        for (iter = sr.begin(); iter != sr.end(); ++iter)
616
        {
617
            MSG_DEBUG("   " << pos << ": id: " << iter->number);
618
            MSG_DEBUG("   " << pos << ": bs: " << iter->bs);
619
            MSG_DEBUG("   " << pos << ": cb: " << iter->cb);
620
            MSG_DEBUG("   " << pos << ": cf: " << iter->cf);
621
            MSG_DEBUG("   " << pos << ": ct: " << iter->ct);
622
            MSG_DEBUG("   " << pos << ": ec: " << iter->ec);
623
            MSG_DEBUG("   " << pos << ": bm: " << iter->bm);
624
            MSG_DEBUG("   " << pos << ": mi: " << iter->mi);
625
            MSG_DEBUG("   " << pos << ": fi: " << iter->fi);
626
            MSG_DEBUG("   " << pos << ": te: " << iter->te);
627
            pos++;
628
        }
629
    }
630
*/
631
    MSG_DEBUG("Added button " << bi << " --> " << na);
632
 
633
    if (index == TExpat::npos)
634
        return oldIndex + 1;
635
 
636
    return index;
637
}
638
 
639
bool TButton::createSoftButton(const EXTBUTTON_t& bt)
640
{
641
    DECL_TRACER("TButton::createSoftButton(const EXTBUTTON_t& bt)");
642
 
643
    if (bt.sr.size() < 2)
644
    {
645
        MSG_ERROR("Button " << bt.bi << ": " << bt.na << " has less than 2 states!");
646
        return false;
647
    }
648
 
649
    MSG_DEBUG("Adding soft button " << bt.bi << ": " << bt.na);
650
    type = bt.type;
651
    bi = bt.bi;
652
    na = bt.na;
653
    lt = mPosLeft = bt.lt;
654
    tp = mPosTop = bt.tp;
655
    wt = bt.wt;
656
    ht = bt.ht;
657
    zo = bt.zo;
658
    hs = bt.hs;
659
    bs = bt.bs;
660
    fb = bt.fb;
661
    ap = bt.ap;
662
    ad = bt.ad;
663
    lp = bt.lp;
664
    lv = bt.lv;
665
    dr = bt.dr;
666
    lu = bt.lu;
667
    ld = bt.ld;
668
    rl = bt.rl;
669
    rh = bt.rh;
670
    rn = bt.rn;
671
    sc = bt.sc;
672
    sr = bt.sr;
673
 
674
    if (gPageManager)
675
        gPageManager->addButtonState(type, ap, ad, ch, cp, lp, lv);
676
 
677
    mChanged = true;
678
    return true;
679
}
680
 
681
BITMAP_t TButton::getLastImage()
682
{
683
    DECL_TRACER("TButton::getLastImage()");
684
 
685
    if (mLastImage.empty())
686
    {
687
        makeElement(mActInstance);
688
 
689
        if (mLastImage.empty())
690
            return BITMAP_t();
691
    }
692
 
693
    BITMAP_t image;
694
    image.buffer = (unsigned char *)mLastImage.getPixels();
695
    image.rowBytes = mLastImage.info().minRowBytes();
696
    image.width = mLastImage.info().width();
697
    image.height = mLastImage.info().height();
698
    return image;
699
}
700
 
701
TBitmap TButton::getLastBitmap()
702
{
703
    DECL_TRACER("TButton::getLastBitmap()");
704
 
705
    if (mLastImage.empty())
706
        makeElement(mActInstance);
707
 
708
    TBitmap bitmap((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
709
    return bitmap;
710
}
711
 
712
FONT_T TButton::getFont()
713
{
714
    DECL_TRACER("TButton::getFont()");
715
 
716
    if (!mFonts)
717
    {
718
        MSG_ERROR("No fonts available!");
719
        return FONT_T();
720
    }
721
 
722
    if (type == LISTBOX && _getGlobalSettings)
723
    {
724
        _getGlobalSettings(this);
725
        mActInstance = 0;
726
    }
727
 
728
    return mFonts->getFont(sr[mActInstance].fi);
729
}
730
 
731
FONT_STYLE TButton::getFontStyle()
732
{
733
    DECL_TRACER("TButton::getFontStyle()");
734
 
735
    if (!mFonts)
736
    {
737
        MSG_ERROR("No fonts available!");
738
        return FONT_NONE;
739
    }
740
 
741
    return mFonts->getStyle(sr[mActInstance].fi);
742
}
743
 
744
void TButton::setBargraphLevel(int level)
745
{
746
    DECL_TRACER("TButton::setBargraphLevel(int level)");
747
 
748
    if (type != BARGRAPH && type != MULTISTATE_BARGRAPH && type != MULTISTATE_GENERAL)
749
        return;
750
 
751
    if (((type == BARGRAPH || type == MULTISTATE_BARGRAPH) && (level < rl || level > rh)) ||
752
        (type == MULTISTATE_GENERAL && (level < 0 || (size_t)level >= sr.size())))
753
    {
754
        MSG_WARNING("Level for bargraph " << na << " is out of range! (" << rl << " to " << rh << " or size " << sr.size() << ")");
755
        return;
756
    }
757
 
758
    TButtonStates *buttonStates = getButtonState();
759
 
760
    if (!buttonStates)
761
    {
762
        MSG_ERROR("Button states not found!");
763
        TError::setError();
764
        return;
765
    }
766
 
767
    int lastLevel = buttonStates->getLastLevel();
768
 
769
    if (((type == BARGRAPH || type == MULTISTATE_BARGRAPH) && lastLevel != level) ||
770
        (type == MULTISTATE_BARGRAPH && mActInstance != level))
771
        mChanged = true;
772
 
773
    if (!mChanged)
774
        return;
775
 
776
    if (type == BARGRAPH)
777
    {
778
        lastLevel = level;
779
        buttonStates->setLastLevel(level);
780
        drawBargraph(mActInstance, level);
781
    }
782
    else if (type == MULTISTATE_BARGRAPH)
783
    {
784
        lastLevel = level;
785
        mActInstance = level;
786
        buttonStates->setLastLevel(level);
787
        drawMultistateBargraph(level);
788
    }
789
    else
790
        setActive(level);
791
}
792
 
793
void TButton::moveBargraphLevel(int x, int y)
794
{
795
    DECL_TRACER("TButton::moveBargraphLevel(int x, int y)");
796
 
797
    if (type != BARGRAPH)
798
        return;
799
 
800
    if (lf.empty())     // Display only
801
        return;
802
 
803
    int level = 0;
804
    int dragUp = false;
805
 
806
    if (dr.compare("horizontal") == 0)
807
    {
808
        level = x;
809
        level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(wt) * static_cast<double>(level));
810
    }
811
    else
812
    {
813
        level = ht - y;
814
        level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(ht) * static_cast<double>(level));
815
    }
816
 
817
    if (lf == "drag" || lf == "dragCenter")
818
    {
819
        int diff = 0;
820
 
821
        level += mBarThreshold;
822
 
823
        if (dr == "horizontal")
824
            dragUp = (mBarStartLevel > level);
825
        else
826
            dragUp = (level > mBarStartLevel);
827
 
828
        diff = (mBarStartLevel > level ? (mBarStartLevel - level) : (level - mBarStartLevel));
829
        double gap = static_cast<double>(rn) / static_cast<double>(rh - rl) * static_cast<double>(diff);
830
        MSG_DEBUG("Gap is " << gap << ", diff: " << diff << ", mBarStartLevel: " << mBarStartLevel << ", level: " << level << ", rn: " << rn);
831
 
832
        if (dragUp)
833
            level = mBarStartLevel + static_cast<int>(gap);
834
        else
835
            level = mBarStartLevel - static_cast<int>(gap);
836
 
837
        if (level < rl)
838
            level = rl;
839
        else if (level > rh)
840
            level = rh;
841
    }
842
 
843
    drawBargraph(mActInstance, level, visible);
844
 
845
    // Send the level
846
    if (lp && lv && gPageManager && gPageManager->getLevelSendState() && gAmxNet)
847
    {
848
        gPageManager->sendLevel(lp, lv, (ri ? ((rh - rl) - level) : level));
849
        TButtonStates *buttonStates = nullptr;
850
 
851
        if ((buttonStates = getButtonState()) != nullptr)
852
            buttonStates->setLastSendLevelX(level);
853
    }
854
}
855
 
856
void TButton::sendJoystickLevels()
857
{
858
    DECL_TRACER("TButton::sendJoystickLevels()");
859
 
860
    if (type != JOYSTICK)
861
        return;
862
 
863
    if (!gAmxNet)
864
    {
865
        MSG_WARNING("The AMX communication thread is not initialized!");
866
        return;
867
    }
868
 
869
    TButtonStates *buttonStates = getButtonState();
870
    int lastJoyX = 0;
871
    int lastJoyY = 0;
872
    int lastSendLevelX = 0;
873
    int lastSendLevelY = 0;
874
 
875
    if (buttonStates)
876
    {
877
        lastJoyX = buttonStates->getLastJoyX();
878
        lastJoyY = buttonStates->getLastJoyY();
879
        lastSendLevelX = buttonStates->getLastSendLevelX();
880
        lastSendLevelY = buttonStates->getLastSendLevelY();
881
    }
882
    else
883
    {
884
        MSG_ERROR("Button states not found!");
885
        return;
886
    }
887
 
888
    // Send the levels
889
    if (lp && lv && gPageManager && gPageManager->getLevelSendState())
890
    {
891
        amx::ANET_SEND scmd;
892
 
893
        scmd.device = TConfig::getChannel();
894
        scmd.port = lp;
895
        scmd.channel = lv;
896
        scmd.level = lv;
897
        scmd.value = (ri ? ((rh - rl) - lastJoyX) : lastJoyX);
898
        scmd.MC = 0x008a;
899
 
900
        if (lastSendLevelX != scmd.value)
901
            gAmxNet->sendCommand(scmd);
902
 
903
        lastSendLevelX = scmd.value;
904
        buttonStates->setLastSendLevelX(scmd.value);
905
 
906
        scmd.channel = lv + 1;
907
        scmd.level = lv + 1;
908
        scmd.value = (ji ? ((rh - rl) - lastJoyY) : lastJoyY);
909
 
910
        if (lastSendLevelY != scmd.value)
911
            gAmxNet->sendCommand(scmd);
912
 
913
        lastSendLevelY = scmd.value;
914
        buttonStates->setLastSendLevelY(lastSendLevelY);
915
    }
916
}
917
 
918
void TButton::sendBargraphLevel()
919
{
920
    DECL_TRACER("TButton::sendBargraphLevel()");
921
 
922
    if (type != BARGRAPH && type != MULTISTATE_BARGRAPH)
923
        return;
924
 
925
    if (!gAmxNet)
926
    {
927
        MSG_WARNING("The AMX communication thread is not initialized!");
928
        return;
929
    }
930
 
931
    TButtonStates *buttonStates = getButtonState();
932
    int lastLevel = 0;
933
    int lastSendLevelX = 0;
934
 
935
    if (buttonStates)
936
    {
937
        lastLevel = buttonStates->getLastLevel();
938
        lastSendLevelX = buttonStates->getLastSendLevelX();
939
    }
940
    else
941
    {
942
        MSG_ERROR("Button states not found!");
943
        return;
944
    }
945
 
946
    // Send the level
947
    if (lp && lv && gPageManager && gPageManager->getLevelSendState())
948
    {
949
        amx::ANET_SEND scmd;
950
 
951
        scmd.device = TConfig::getChannel();
952
        scmd.port = lp;
953
        scmd.channel = lv;
954
        scmd.level = lv;
955
        scmd.value = (ri ? ((rh - rl) - lastLevel) : lastLevel);
956
        scmd.MC = 0x008a;
957
 
958
        if (lastSendLevelX != lastLevel)
959
            gAmxNet->sendCommand(scmd);
960
 
961
        lastSendLevelX = lastLevel;
962
 
963
        if (buttonStates)
964
            buttonStates->setLastSendLevelX(lastSendLevelX);
965
    }
966
}
967
 
968
bool TButton::invalidate()
969
{
970
    DECL_TRACER("TButton::invalidate()");
971
 
972
    if (prg_stopped)
973
        return true;
974
 
975
    ulong parent = mHandle & 0xffff0000;
976
    THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
977
 
978
    if (tr && tr->mImageRefresh)
979
    {
980
        if (tr->mImageRefresh->isRunning())
981
            tr->mImageRefresh->stop();
982
    }
983
 
984
    if (type == TEXT_INPUT)
985
    {
986
        if (gPageManager && gPageManager->getCallDropButton())
987
            gPageManager->getCallDropButton()(mHandle);
988
    }
989
 
990
    visible = false;
991
    return true;
992
}
993
 
994
string& TButton::getDrawOrder(int instance)
995
{
996
    DECL_TRACER("TButton::getDrawOrder(int instance)");
997
 
998
    if (instance < 0 || (size_t)instance > sr.size())
999
    {
1000
        MSG_ERROR("Instance is out of range!");
1001
        return dummy;
1002
    }
1003
 
1004
    return sr[instance]._do;
1005
}
1006
 
1007
BUTTONTYPE TButton::getButtonType(const string& bt)
1008
{
1009
    DECL_TRACER("TButton::getButtonType(const string& bt)");
1010
 
1011
    if (strCaseCompare(bt, "general") == 0)
1012
        return GENERAL;
1013
    else if (strCaseCompare(bt, "multi-state general") == 0 || strCaseCompare(bt, "multiGeneral") == 0)
1014
        return MULTISTATE_GENERAL;
1015
    else if (strCaseCompare(bt, "bargraph") == 0)
1016
        return BARGRAPH;
1017
    else if (strCaseCompare(bt, "multi-state bargraph") == 0 || strCaseCompare(bt, "multiBargraph") == 0)
1018
        return MULTISTATE_BARGRAPH;
1019
    else if (strCaseCompare(bt, "joystick") == 0)
1020
        return JOYSTICK;
1021
    else if (strCaseCompare(bt, "text input") == 0 || strCaseCompare(bt, "textArea") == 0)
1022
        return TEXT_INPUT;
1023
    else if (strCaseCompare(bt, "computer control") == 0)
1024
        return COMPUTER_CONTROL;
1025
    else if (strCaseCompare(bt, "take note") == 0)
1026
        return TAKE_NOTE;
1027
    else if (strCaseCompare(bt, "sub-page view") == 0 || strCaseCompare(bt, "subPageView") == 0)
1028
        return SUBPAGE_VIEW;
1029
    else if (strCaseCompare(bt, "listBox") == 0)
1030
        return LISTBOX;
1031
 
1032
    return NONE;
1033
}
1034
 
1035
string TButton::buttonTypeToString()
1036
{
1037
    return buttonTypeToString(type);
1038
}
1039
 
1040
string TButton::buttonTypeToString(BUTTONTYPE t)
1041
{
1042
    switch(t)
1043
    {
1044
        case NONE:                  return "NONE";
1045
        case GENERAL:               return "GENERAL";
1046
        case MULTISTATE_GENERAL:    return "MULTISTAE GENERAL";
1047
        case BARGRAPH:              return "BARGRAPH";
1048
        case MULTISTATE_BARGRAPH:   return "MULTISTATE BARGRAPH";
1049
        case JOYSTICK:              return "JOISTICK";
1050
        case TEXT_INPUT:            return "TEXT INPUT";
1051
        case COMPUTER_CONTROL:      return "COMPUTER CONTROL";
1052
        case TAKE_NOTE:             return "TAKE NOTE";
1053
        case SUBPAGE_VIEW:          return "SUBPAGE VIEW";
1054
        case LISTBOX:               return "LISTBOX";
1055
    }
1056
 
1057
    return "";
1058
}
1059
 
1060
FEEDBACK TButton::getButtonFeedback(const string& fb)
1061
{
1062
    DECL_TRACER("TButton::getButtonFeedback(const string& fb)");
1063
 
1064
    if (fb.compare("channel") == 0)
1065
        return FB_CHANNEL;
1066
    else if (fb.compare("inverted channel") == 0)
1067
        return FB_INV_CHANNEL;
1068
    else if (fb.compare("always on") == 0)
1069
        return FB_ALWAYS_ON;
1070
    else if (fb.compare("momentary") == 0)
1071
        return FB_MOMENTARY;
1072
    else if (fb.compare("blink") == 0)
1073
        return FB_BLINK;
1074
 
1075
    return FB_NONE;
1076
}
1077
 
1078
bool TButton::createButtons(bool force)
1079
{
1080
    DECL_TRACER("TButton::createButtons(bool force)");
1081
 
1082
    if (prg_stopped)
1083
        return false;
1084
 
1085
    if (force)
1086
    {
1087
        mChanged = true;
1088
        MSG_TRACE("Creating of image is forced!");
1089
    }
1090
 
1091
    // Get the images, if there any
1092
    if (sr.empty())
1093
        return true;
1094
 
1095
    vector<SR_T>::iterator srIter;
1096
 
1097
    for (srIter = sr.begin(); srIter != sr.end(); ++srIter)
1098
    {
1099
        int number = srIter->number;
1100
 
1101
        if (srIter->sb > 0)
1102
            continue;
1103
 
1104
        bool bmExistMi = false;
1105
        bool bmExistBm = false;
1106
        bool reload = false;
1107
 
1108
        if (!srIter->mi.empty())
1109
        {
1110
            if ((bmExistMi = TImgCache::existBitmap(srIter->mi, _BMTYPE_CHAMELEON)) == false)
1111
            {
1112
                mChanged = true;
1113
                reload = true;
1114
            }
1115
        }
1116
 
1117
        if (!srIter->bm.empty())
1118
        {
1119
            if ((bmExistBm = TImgCache::existBitmap(srIter->bm, _BMTYPE_BITMAP)) == false)
1120
            {
1121
                mChanged = true;
1122
                reload = true;
1123
            }
1124
        }
1125
 
1126
        if (!force)
1127
        {
1128
            if (!reload)   // If the image already exist, do not load it again.
1129
                continue;
1130
        }
1131
 
1132
        if (!bmExistMi && !srIter->mi.empty())        // Do we have a chameleon image?
1133
        {
1134
            sk_sp<SkData> image;
1135
            SkBitmap bm;
1136
 
1137
            if (!(image = readImage(srIter->mi)))
1138
                return false;
1139
 
1140
            DecodeDataToBitmap(image, &bm);
1141
 
1142
            if (bm.empty())
1143
            {
1144
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
1145
                return false;
1146
            }
1147
 
1148
            TImgCache::addImage(srIter->mi, bm, _BMTYPE_CHAMELEON);
1149
            srIter->mi_width = bm.info().width();
1150
            srIter->mi_height = bm.info().height();
1151
            mChanged = true;
1152
        }
1153
 
1154
        if (!bmExistBm && !srIter->bm.empty())        // Do we have a bitmap?
1155
        {
1156
            sk_sp<SkData> image;
1157
            SkBitmap bm;
1158
 
1159
            if (!(image = readImage(srIter->bm)))
1160
                return false;
1161
 
1162
            DecodeDataToBitmap(image, &bm);
1163
 
1164
            if (bm.empty())
1165
            {
1166
                MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
1167
                return false;
1168
            }
1169
 
1170
            TImgCache::addImage(srIter->bm, bm, _BMTYPE_BITMAP);
1171
            srIter->bm_width = bm.info().width();
1172
            srIter->bm_height = bm.info().height();
1173
            mChanged = true;
1174
        }
1175
    }
1176
 
1177
    return true;
1178
}
1179
 
1180
void TButton::refresh()
1181
{
1182
    DECL_TRACER("TButton::refresh()");
1183
 
1184
    mChanged = true;
1185
    makeElement();
1186
}
1187
 
1188
bool TButton::makeElement(int instance)
1189
{
1190
    DECL_TRACER("TButton::makeElement(int instance)");
1191
 
1192
    if (prg_stopped)
1193
        return false;
1194
 
1195
    int inst = mActInstance;
1196
 
1197
    if (instance >= 0 && static_cast<size_t>(instance) < sr.size())
1198
    {
1199
        if (mActInstance != instance)
1200
            mChanged = true;
1201
 
1202
        inst = instance;
1203
    }
1204
    else if (inst < 0 || static_cast<size_t>(inst) >= sr.size())
1205
        inst = mActInstance = 0;
1206
 
1207
    int lastLevel = 0;
1208
    int lastJoyX = 0;
1209
    int lastJoyY = 0;
1210
    TButtonStates *buttonStates = nullptr;
1211
    bool isSystem = isSystemButton();
1212
 
1213
    if (type == BARGRAPH || type == JOYSTICK || type == MULTISTATE_BARGRAPH)
1214
    {
1215
        TButtonStates *buttonStates = getButtonState();
1216
 
1217
        if (buttonStates)
1218
        {
1219
            lastLevel = buttonStates->getLastLevel();
1220
            lastJoyX = buttonStates->getLastJoyX();
1221
            lastJoyY = buttonStates->getLastJoyY();
1222
            MSG_DEBUG("lastLevel: " << lastLevel << ", lastJoyX: " << lastJoyX << ", lastJoyY: " << lastJoyY);
1223
        }
1224
        else
1225
        {
1226
            MSG_ERROR("Button states not found!");
1227
            return false;
1228
        }
1229
    }
1230
 
1231
    if (type == MULTISTATE_GENERAL && ar == 1)
1232
        return drawButtonMultistateAni();
1233
    else if (type == BARGRAPH && isSystem && lv == 9)   // System volume button
1234
        return drawBargraph(inst, TConfig::getSystemVolume());
1235
    else if (type == BARGRAPH)
1236
    {
1237
        if (lf == "center" || lf == "dragCenter")
1238
            lastLevel = (rh - rl) / 2;
1239
 
1240
        return drawBargraph(inst, lastLevel);
1241
    }
1242
    else if (type == MULTISTATE_BARGRAPH)
1243
        return drawMultistateBargraph(lastLevel, true);
1244
    else if (type == TEXT_INPUT)
1245
    {
1246
        if (isSystem && !mSystemReg)
1247
        {
1248
            registerSystemButton();
1249
            mChanged = true;
1250
        }
1251
 
1252
        drawTextArea(inst);
1253
        mActInstance = inst;
1254
    }
1255
    else if (type == LISTBOX)
1256
    {
1257
        if (_getListContent && !mSystemReg)
1258
        {
1259
            mListContent = _getListContent(mHandle, ap, ta, ti, tr, tc);
1260
            mChanged = true;
1261
        }
1262
 
1263
        if (isSystem)
1264
            mSystemReg = true;
1265
 
1266
        drawList();
1267
    }
1268
    else if (isSystem && type == GENERAL)
1269
    {
1270
        TConfig::setTemporary(true);
1271
 
1272
        if (isSystemCheckBox(ch))
1273
        {
1274
            int in = getButtonInstance(0, ch);
1275
 
1276
            if (in >= 0)
1277
            {
1278
                inst = mActInstance = in;
1279
#ifndef __ANDROID__
1280
                if (ch == SYSTEM_ITEM_VIEWSCALEFIT && sr[0].oo < 0) // scale to fit disabled
1281
                {
1282
                    sr[0].oo = 128;
1283
                    mChanged = true;
1284
                }
1285
#else
1286
                if (ch == SYSTEM_ITEM_VIEWBANNER && sr[0].oo < 0)   // show banner disabled
1287
                {
1288
                    sr[0].oo = 128;
1289
                    mChanged = true;
1290
                }
1291
#endif
1292
                if (ch == SYSTEM_ITEM_VIEWTOOLBAR)  // Force toolbar is only available if toolbar is not suppressed
1293
                {
1294
                    if (TConfig::getToolbarSuppress() && sr[0].oo < 0)
1295
                    {
1296
                        sr[0].oo = 128;
1297
                        mChanged = true;
1298
                    }
1299
                    else if (!TConfig::getToolbarSuppress() && sr[0].oo > 0)
1300
                    {
1301
                        sr[0].oo = -1;
1302
                        mChanged = true;
1303
                    }
1304
                }
1305
            }
1306
        }
1307
        else if (isSystemTextLine(ad) && ad != SYSTEM_ITEM_FTPSURFACE)
1308
        {
1309
            sr[0].te = sr[1].te = fillButtonText(ad, 0);
1310
            mChanged = true;
1311
        }
1312
 
1313
        TConfig::setTemporary(false);
1314
 
1315
        if (mLastImage.empty())
1316
            mChanged = true;
1317
 
1318
        MSG_DEBUG("Drawing system button " << ch << " with instance " << inst);
1319
        return drawButton(inst);
1320
    }
1321
    else if (type == JOYSTICK)
1322
    {
1323
        if (lf == "center" || lf == "dragCenter")
1324
            lastJoyX = lastJoyY = (rh - rl) / 2;
1325
 
1326
        if (buttonStates)
1327
        {
1328
            buttonStates->setLastJoyX(lastJoyX);
1329
            buttonStates->setLastJoyY(lastJoyY);
1330
        }
1331
 
1332
        return drawJoystick(lastJoyX, lastJoyY);
1333
    }
1334
    else
1335
    {
1336
        if (mLastImage.empty())
1337
            mChanged = true;
1338
 
1339
        return drawButton(inst);
1340
    }
1341
 
1342
    return false;
1343
}
1344
 
1345
bool TButton::setActive(int instance)
1346
{
1347
    DECL_TRACER("TButton::setActive(int instance)");
1348
 
1349
    if (mAniRunning)
1350
    {
1351
#if TESTMODE == 1
1352
        setScreenDone();
1353
#endif
1354
        return true;
1355
    }
1356
 
1357
    if (instance < 0 || (size_t)instance >= sr.size())
1358
    {
1359
        MSG_ERROR("Instance " << instance << " is out of range from 0 to " << sr.size() << "!");
1360
#if TESTMODE == 1
1361
        setScreenDone();
1362
#endif
1363
        return false;
1364
    }
1365
 
1366
    if (instance == mActInstance && !mLastImage.empty())
1367
    {
1368
#if TESTMODE == 1
1369
        __success = true;
1370
        setScreenDone();
1371
#endif
1372
        return true;
1373
    }
1374
 
1375
    mActInstance = instance;
1376
    mChanged = true;
1377
    makeElement(instance);
1378
 
1379
    return true;
1380
}
1381
 
1382
bool TButton::setIcon(int id, int instance)
1383
{
1384
    DECL_TRACER("TButton::setIcon(int id, int instance)");
1385
 
1386
    if (instance >= 0 && (size_t)instance >= sr.size())
1387
    {
1388
        MSG_ERROR("Instance " << instance << " does not exist!");
1389
        return false;
1390
    }
1391
 
1392
    int inst = instance;
1393
    int loop = 1;
1394
 
1395
    if (inst < 0)
1396
    {
1397
        loop = (int)sr.size();
1398
        inst = 0;
1399
    }
1400
 
1401
    for (int i = 0; i < loop; ++i)
1402
    {
1403
        if (sr[inst].ii != id)
1404
            mChanged = true;
1405
 
1406
        sr[inst].ii = id;
1407
        inst++;
1408
    }
1409
 
1410
    return makeElement(instance);
1411
}
1412
 
1413
bool TButton::setIcon(const string& icon, int instance)
1414
{
1415
    DECL_TRACER("TButton::setIcon(const string& icon, int instance)");
1416
 
1417
    if (instance >= 0 && (size_t)instance >= sr.size())
1418
    {
1419
        MSG_ERROR("Instance " << instance << " does not exist!");
1420
        return false;
1421
    }
1422
 
1423
    if (!gIcons)
1424
    {
1425
        gIcons = new TIcons();
1426
 
1427
        if (TError::isError())
1428
        {
1429
            MSG_ERROR("Error initializing icons!");
1430
            return false;
1431
        }
1432
    }
1433
 
1434
    int id = gIcons->getNumber(icon);
1435
 
1436
    if (id == -1)
1437
    {
1438
        MSG_WARNING("Icon " << icon << " not found!");
1439
        return false;
1440
    }
1441
 
1442
    int inst = instance;
1443
    int loop = 1;
1444
 
1445
    if (inst < 0)
1446
    {
1447
        loop = (int)sr.size();
1448
        inst = 0;
1449
    }
1450
 
1451
    for (int i = 0; i < loop; ++i)
1452
    {
1453
        if (sr[inst].ii == id)
1454
        {
1455
            inst++;
1456
            continue;
1457
        }
1458
 
1459
        if (sr[inst].ii != id)
1460
            mChanged = true;
1461
 
1462
        sr[inst].ii = id;
1463
        inst++;
1464
    }
1465
 
1466
    return makeElement(instance);
1467
}
1468
 
1469
bool TButton::revokeIcon(int instance)
1470
{
1471
    DECL_TRACER("TButton::revokeIcon(int instance)");
1472
 
1473
    if (instance >= 0 && (size_t)instance >= sr.size())
1474
    {
1475
        MSG_ERROR("Instance " << instance << " does not exist!");
1476
        return false;
1477
    }
1478
 
1479
    int inst = instance;
1480
    int loop = 1;
1481
 
1482
    if (inst < 0)
1483
    {
1484
        loop = (int)sr.size();
1485
        inst = 0;
1486
    }
1487
 
1488
    for (int i = 0; i < loop; ++i)
1489
    {
1490
        if (sr[inst].ii == 0)
1491
        {
1492
            inst++;
1493
            continue;
1494
        }
1495
 
1496
        if (sr[inst].ii != 0)
1497
            mChanged = true;
1498
 
1499
        sr[inst].ii = 0;
1500
        inst++;
1501
    }
1502
 
1503
    return makeElement(instance);
1504
}
1505
 
1506
bool TButton::setText(const string& txt, int instance)
1507
{
1508
    DECL_TRACER("TButton::setText(const string& txt, int instance)");
1509
 
1510
    if (instance >= 0 && (size_t)instance >= sr.size())
1511
    {
1512
        MSG_ERROR("Instance " << instance << " does not exist!");
1513
#if TESTMODE == 1
1514
        setAllDone();
1515
#endif
1516
        return false;
1517
    }
1518
 
1519
    if (!setTextOnly(txt, instance))
1520
    {
1521
#if TESTMODE == 1
1522
        setAllDone();
1523
#endif
1524
        return false;
1525
    }
1526
 
1527
    if (!mChanged)      // Do not try to redraw the button if nothing changed
1528
    {
1529
#if TESTMODE == 1
1530
        MSG_INFO("Nothing changed!");
1531
        __success = true;
1532
        setScreenDone();
1533
#endif
1534
        return true;
1535
    }
1536
 
1537
    return makeElement(instance);
1538
}
1539
 
1540
bool TButton::setTextOnly(const string& txt, int instance)
1541
{
1542
    DECL_TRACER("TButton::setTextOnly(const string& txt, int instance)");
1543
 
1544
    if (instance >= 0 && (size_t)instance >= sr.size())
1545
    {
1546
        MSG_ERROR("Instance " << instance << " does not exist!");
1547
        return false;
1548
    }
1549
 
1550
    MSG_DEBUG("Setting text to: " << txt);
1551
 
1552
    if (instance < 0)
1553
    {
1554
        for (size_t i = 0; i < sr.size(); ++i)
1555
        {
1556
            if (sr[i].te != txt && static_cast<int>(i) == mActInstance)
1557
                mChanged = true;
1558
 
1559
            sr[i].te = txt;
1560
        }
1561
    }
1562
    else
1563
    {
1564
        if (sr[instance].te != txt && static_cast<int>(instance) == mActInstance)
1565
            mChanged = true;
1566
 
1567
        sr[instance].te = txt;
1568
    }
1569
 
1570
    if (instance <= 0 && isSystemButton())
1571
    {
1572
        bool temp = TConfig::setTemporary(true);
1573
        // If we've an input line or the text line of a "combobox" then we'll
1574
        // save the changed value here.
1575
        switch(ad)
1576
        {
1577
            case SYSTEM_ITEM_NETLINX_IP:        TConfig::saveController(txt); break;
1578
            case SYSTEM_ITEM_NETLINX_CHANNEL:   TConfig::saveChannel(atoi(txt.c_str())); break;
1579
            case SYSTEM_ITEM_NETLINX_PORT:      TConfig::savePort(atoi(txt.c_str())); break;
1580
            case SYSTEM_ITEM_NETLINX_PTYPE:     TConfig::savePanelType(txt); break;
1581
 
1582
            case SYSTEM_ITEM_SYSTEMSOUND:       TConfig::saveSystemSoundFile(txt); break;
1583
            case SYSTEM_ITEM_SINGLEBEEP:        TConfig::saveSingleBeepFile(txt); break;
1584
            case SYSTEM_ITEM_DOUBLEBEEP:        TConfig::saveDoubleBeepFile(txt); break;
1585
 
1586
            case SYSTEM_ITEM_SIPPROXY:          TConfig::setSIPproxy(txt); break;
1587
            case SYSTEM_ITEM_SIPPORT:           TConfig::setSIPport(atoi(txt.c_str())); break;
1588
            case SYSTEM_ITEM_SIPSTUN:           TConfig::setSIPstun(txt); break;
1589
            case SYSTEM_ITEM_SIPDOMAIN:         TConfig::setSIPdomain(txt); break;
1590
            case SYSTEM_ITEM_SIPUSER:           TConfig::setSIPuser(txt); break;
1591
            case SYSTEM_ITEM_SIPPASSWORD:       TConfig::setSIPpassword(txt); break;
1592
 
1593
            case SYSTEM_ITEM_LOGLOGFILE:        TConfig::saveLogFile(txt); break;
1594
 
1595
            case SYSTEM_ITEM_FTPUSER:           TConfig::saveFtpUser(txt); break;
1596
            case SYSTEM_ITEM_FTPPASSWORD:       TConfig::saveFtpPassword(txt); break;
1597
            case SYSTEM_ITEM_FTPSURFACE:        TConfig::saveFtpSurface(txt); break;
1598
        }
1599
 
1600
        TConfig::setTemporary(temp);
1601
    }
1602
 
1603
    return true;
1604
}
1605
 
1606
bool TButton::appendText(const string &txt, int instance)
1607
{
1608
    DECL_TRACER("TButton::appendText(const string &txt, int instance)");
1609
 
1610
    if (instance >= 0 && (size_t)instance >= sr.size())
1611
    {
1612
        MSG_ERROR("Instance " << instance << " does not exist!");
1613
        return false;
1614
    }
1615
 
1616
    if (txt.empty())
1617
    {
1618
#if TESTMODE == 1
1619
        __success = true;
1620
        __done = true;
1621
#endif
1622
        return true;
1623
    }
1624
 
1625
    if (instance < 0)
1626
    {
1627
        for (size_t i = 0; i < sr.size(); ++i)
1628
            sr[i].te.append(txt);
1629
    }
1630
    else
1631
        sr[instance].te.append(txt);
1632
 
1633
    mChanged = true;
1634
    return makeElement(instance);
1635
}
1636
 
1637
void TButton::setTextCursorPosition(int oldPos, int newPos)
1638
{
1639
    DECL_TRACER("TButton::setTextCursorPosition(int oldPos, int newPos)");
1640
 
1641
    if (type != TEXT_INPUT)
1642
        return;
1643
 
1644
    if (oldPos == newPos && newPos == mCursorPosition)
1645
        return;
1646
 
1647
    mCursorPosition = newPos;
1648
}
1649
 
1650
void TButton::setTextFocus(bool in)
1651
{
1652
    DECL_TRACER("TButton::setTextFocus(bool in)");
1653
 
1654
    if (type != TEXT_INPUT)
1655
        return;
1656
 
1657
    mHasFocus = in;
1658
 
1659
    if (mHasFocus && mActInstance != STATE_ON)
1660
        makeElement(STATE_ON);
1661
    else if (!mHasFocus && mActInstance != STATE_OFF)
1662
        makeElement(STATE_OFF);
1663
}
1664
 
1665
bool TButton::setBorderColor(const string &color, int instance)
1666
{
1667
    DECL_TRACER("TButton::setBorderColor(const string &color, int instance)");
1668
 
1669
    if (instance >= 0 && (size_t)instance >= sr.size())
1670
    {
1671
        MSG_ERROR("Instance " << instance << " does not exist!");
1672
#if TESTMODE == 1
1673
        setScreenDone();
1674
#endif
1675
        return false;
1676
    }
1677
 
1678
    if (instance < 0)
1679
    {
1680
        for (size_t i = 0; i < sr.size(); ++i)
1681
        {
1682
            if (sr[i].cb.compare(color) == 0)
1683
                continue;
1684
 
1685
            if ((int)i == mActInstance)
1686
                mChanged = true;
1687
 
1688
            sr[i].cb = color;
1689
        }
1690
    }
1691
    else if (sr[instance].cb != color)
1692
    {
1693
        if (mActInstance != instance)
1694
            mChanged = true;
1695
 
1696
        sr[instance].cb = color;
1697
    }
1698
 
1699
    if (!mChanged)
1700
    {
1701
#if TESTMODE == 1
1702
        __success = true;
1703
        setScreenDone();
1704
#endif
1705
        return true;
1706
    }
1707
 
1708
    return makeElement(instance);
1709
}
1710
 
1711
string TButton::getBorderColor(int instance)
1712
{
1713
    DECL_TRACER("TButton::getBorderColor(int instance)");
1714
 
1715
    if (instance < 0 || (size_t)instance >= sr.size())
1716
    {
1717
        MSG_ERROR("Instance " << instance << " does not exist!");
1718
        return string();
1719
    }
1720
 
1721
    return sr[instance].cb;
1722
}
1723
 
1724
bool TButton::setFillColor(const string& color, int instance)
1725
{
1726
    DECL_TRACER("TButton::setFillColor(const string& color, int instance)");
1727
 
1728
    if (instance >= 0 && (size_t)instance >= sr.size())
1729
    {
1730
        MSG_ERROR("Instance " << instance << " does not exist!");
1731
        return false;
1732
    }
1733
 
1734
    if (instance < 0)
1735
    {
1736
        for (size_t i = 0; i < sr.size(); ++i)
1737
        {
1738
            if (sr[i].cf == color)
1739
                continue;
1740
 
1741
            if ((int)i == mActInstance)
1742
                mChanged = true;
1743
 
1744
            sr[i].cf = color;
1745
        }
1746
    }
1747
    else if (sr[instance].cf != color)
1748
    {
1749
        if (mActInstance != instance)
1750
            mChanged = true;
1751
 
1752
        sr[instance].cf = color;
1753
    }
1754
 
1755
    if (!mChanged)
1756
    {
1757
#if TESTMODE == 1
1758
        __success = true;
1759
        setScreenDone();
1760
#endif
1761
        return true;
1762
    }
1763
 
1764
    return makeElement(instance);
1765
}
1766
 
1767
bool TButton::setTextColor(const string& color, int instance)
1768
{
1769
    DECL_TRACER("TButton::setTextColor(const string& color, int instance)");
1770
 
1771
    if (!setTextColorOnly(color, instance))
1772
        return false;
1773
 
1774
    if (!mChanged)
1775
    {
1776
#if TESTMODE == 1
1777
        __success = true;
1778
        setScreenDone();
1779
#endif
1780
        return true;
1781
    }
1782
 
1783
    return makeElement(instance);
1784
}
1785
 
1786
bool TButton::setTextColorOnly(const string& color, int instance)
1787
{
1788
    DECL_TRACER("TButton::setTextColorOnly(const string& color, int instance)");
1789
 
1790
    if (instance >= 0 && (size_t)instance >= sr.size())
1791
    {
1792
        MSG_ERROR("Instance " << instance << " does not exist!");
1793
        return false;
1794
    }
1795
 
1796
    if (instance < 0)
1797
    {
1798
        for (size_t i = 0; i < sr.size(); ++i)
1799
        {
1800
            if (sr[i].ct == color)
1801
                continue;
1802
 
1803
            if ((int)i == mActInstance)
1804
                mChanged = true;
1805
 
1806
            sr[i].ct = color;
1807
        }
1808
    }
1809
    else if (sr[instance].ct != color)
1810
    {
1811
        if (mActInstance == instance)
1812
            mChanged = true;
1813
 
1814
        sr[instance].ct = color;
1815
    }
1816
 
1817
    return true;
1818
}
1819
 
1820
bool TButton::setDrawOrder(const string& order, int instance)
1821
{
1822
    DECL_TRACER("TButton::setDrawOrder(const string& order, int instance)");
1823
 
1824
    if (instance >= 0 && (size_t)instance >= sr.size())
1825
    {
1826
        MSG_ERROR("Instance " << instance << " does not exist!");
1827
        return false;
1828
    }
1829
 
1830
    if (instance < 0)
1831
    {
1832
        for (size_t i = 0; i < sr.size(); ++i)
1833
        {
1834
            if (sr[i]._do == order)
1835
                continue;
1836
 
1837
            if ((int)i == mActInstance)
1838
                mChanged = true;
1839
 
1840
            sr[i]._do = order;
1841
        }
1842
    }
1843
    else if (sr[instance]._do != order)
1844
    {
1845
        if (mActInstance == instance)
1846
            mChanged = true;
1847
 
1848
        sr[instance]._do = order;
1849
    }
1850
 
1851
    if (!mChanged)
1852
    {
1853
#if TESTMODE == 1
1854
        __success = true;
1855
        setScreenDone();
1856
#endif
1857
        return true;
1858
    }
1859
 
1860
    return makeElement(instance);
1861
}
1862
 
1863
FEEDBACK TButton::getFeedback()
1864
{
1865
    DECL_TRACER("TButton::getFeedback()");
1866
 
1867
    if (type != GENERAL)
1868
        return FB_NONE;
1869
 
1870
    return fb;
1871
}
1872
 
1873
bool TButton::setFeedback(FEEDBACK feedback)
1874
{
1875
    DECL_TRACER("TButton::setFeedback(FEEDBACK feedback)");
1876
 
1877
    if (type != GENERAL)
1878
    {
1879
#if TESTMODE == 1
1880
        setAllDone();
1881
#endif
1882
        return false;
1883
    }
1884
 
1885
    int oldFB = fb;
1886
    fb = feedback;
1887
 
1888
    if (mEnabled && !hd)
1889
    {
1890
        if ((feedback == FB_ALWAYS_ON || feedback == FB_INV_CHANNEL) && mActInstance != 1)
1891
        {
1892
            mActInstance = 1;
1893
            mChanged = true;
1894
            makeElement(1);
1895
        }
1896
        else if (oldFB == FB_ALWAYS_ON && feedback != FB_ALWAYS_ON && feedback != FB_INV_CHANNEL && mActInstance == 1)
1897
        {
1898
            mActInstance = 0;
1899
            mChanged = true;
1900
            makeElement(0);
1901
        }
1902
    }
1903
#if TESTMODE == 1
1904
    if (!mChanged)
1905
        __success = true;
1906
 
1907
    setScreenDone();
1908
#endif
1909
    return true;
1910
}
1911
 
1912
bool TButton::setBorderStyle(const string& style, int instance)
1913
{
1914
    DECL_TRACER("TButton::setBorderStyle(const string& style, int instance)");
1915
 
1916
    if (instance >= 0 && (size_t)instance >= sr.size())
1917
    {
1918
        MSG_ERROR("Instance " << instance << " does not exist!");
1919
        return false;
1920
    }
1921
 
1922
    mChanged = true;
1923
    MSG_DEBUG("Setting border " << style);
1924
 
1925
    if (strCaseCompare(style, "None") == 0)     // Clear the border?
1926
    {
1927
        if (instance < 0)
1928
        {
1929
            bs.clear();
1930
 
1931
            for (size_t i = 0; i < sr.size(); ++i)
1932
                sr[i].bs.clear();
1933
        }
1934
        else
1935
        {
1936
            sr[instance].bs.clear();
1937
            bs.clear();
1938
        }
1939
 
1940
        if (mEnabled && !hd)
1941
            makeElement(instance);
1942
 
1943
        return true;
1944
    }
1945
 
1946
    // Look in the system table and try to find the border.
1947
    if (gPageManager && gPageManager->getSystemDraw())
1948
    {
1949
        if (gPageManager->getSystemDraw()->existBorder(style))
1950
        {
1951
            if (instance < 0)
1952
            {
1953
                bs = style;
1954
 
1955
                for (size_t i = 0; i < sr.size(); ++i)
1956
                    sr[i].bs = style;
1957
            }
1958
            else
1959
            {
1960
                sr[instance].bs = style;
1961
 
1962
                if (bs != style)
1963
                    bs.clear();
1964
            }
1965
 
1966
            if (mEnabled && !hd)
1967
                makeElement(instance);
1968
 
1969
            return true;
1970
        }
1971
    }
1972
 
1973
    // Check whether it is a supported style or not. If the style is not
1974
    // supported, it will be ignored.
1975
    string corrName = getCorrectName(style);
1976
 
1977
    if (!style.empty())
1978
    {
1979
        if (instance < 0)
1980
        {
1981
            bs = corrName;
1982
 
1983
            for (size_t i = 0; i < sr.size(); ++i)
1984
                sr[i].bs = corrName;
1985
        }
1986
        else
1987
        {
1988
            sr[instance].bs = corrName;
1989
 
1990
            if (bs != corrName)
1991
                bs.clear();
1992
        }
1993
 
1994
        if (mEnabled && !hd)
1995
            makeElement(instance);
1996
 
1997
        return true;
1998
    }
1999
#if TESTMODE == 1
2000
    __done = true;
2001
#endif
2002
    return false;
2003
}
2004
 
2005
bool TButton::setBorderStyle(int style, int instance)
2006
{
2007
    DECL_TRACER("TButton::setBorderStyle(int style, int instance)");
2008
 
2009
    if (instance >= 0 && (size_t)instance >= sr.size())
2010
    {
2011
        MSG_ERROR("Instance " << instance << " does not exist!");
2012
        return false;
2013
    }
2014
 
2015
    if (style == 0)     // Clear the border?
2016
    {
2017
        if (instance < 0)
2018
        {
2019
            bs.clear();
2020
 
2021
            for (size_t i = 0; i < sr.size(); ++i)
2022
            {
2023
                if (!sr[i].bs.empty())
2024
                    mChanged = true;
2025
 
2026
                sr[i].bs.clear();
2027
            }
2028
 
2029
            if (!bs.empty())
2030
                mChanged = true;
2031
 
2032
            bs.clear();
2033
        }
2034
        else
2035
        {
2036
            if (!sr[instance].bs.empty())
2037
                mChanged = true;
2038
 
2039
            sr[instance].bs.clear();
2040
            bs.clear();
2041
        }
2042
 
2043
        if (mEnabled && !hd)
2044
            makeElement(instance);
2045
 
2046
        return true;
2047
    }
2048
 
2049
    string st = getBorderName(style);
2050
 
2051
    if (st.empty())
2052
    {
2053
        MSG_WARNING("The index " << style << " is not supported!");
2054
#if TESTMODE == 1
2055
        setAllDone();
2056
#endif
2057
        return false;
2058
    }
2059
 
2060
    // Look in the system table and try to find the border.
2061
    if (gPageManager && gPageManager->getSystemDraw())
2062
    {
2063
        if (gPageManager->getSystemDraw()->existBorder(st))
2064
        {
2065
            MSG_DEBUG("Found frame " << st << " and draw it ...");
2066
 
2067
            if (instance < 0)
2068
            {
2069
                bs = st;
2070
 
2071
                for (size_t i = 0; i < sr.size(); ++i)
2072
                    sr[i].bs = st;
2073
            }
2074
            else
2075
            {
2076
                sr[instance].bs = st;
2077
 
2078
                if (bs != st)
2079
                    bs.clear();
2080
            }
2081
 
2082
            mChanged = true;
2083
 
2084
            if (mEnabled && !hd)
2085
                makeElement(instance);
2086
 
2087
            return true;
2088
        }
2089
    }
2090
 
2091
    // Check whether it is a supported style or not. If the style is not
2092
    // supported, it will be ignored.
2093
    if (instance < 0)
2094
    {
2095
        bs = st;
2096
 
2097
        for (size_t i = 0; i < sr.size(); ++i)
2098
            sr[i].bs = st;
2099
    }
2100
    else
2101
    {
2102
        sr[instance].bs = st;
2103
 
2104
        if (bs != st)
2105
            bs.clear();
2106
    }
2107
 
2108
    mChanged = true;
2109
 
2110
    if (mEnabled && !hd)
2111
        makeElement(instance);
2112
 
2113
    return true;
2114
}
2115
 
2116
string TButton::getBorderStyle(int instance)
2117
{
2118
    DECL_TRACER("TButton::getBorderStyle(int instance)");
2119
 
2120
    if (instance < 0 || instance >= (int)sr.size())
2121
    {
2122
        MSG_ERROR("Invalid instance " << (instance + 1) << " submitted!");
2123
        return string();
2124
    }
2125
 
2126
    if (sr[instance].bs.empty())
2127
        return bs;
2128
 
2129
    return sr[instance].bs;
2130
}
2131
 
2132
bool TButton::setBargraphUpperLimit(int limit)
2133
{
2134
    DECL_TRACER("TButton::setBargraphUpperLimit(int limit)");
2135
 
2136
    if (limit < 1 || limit > 65535)
2137
    {
2138
        MSG_ERROR("Invalid upper limit " << limit);
2139
        return false;
2140
    }
2141
 
2142
    rh = limit;
2143
    return true;
2144
}
2145
 
2146
bool TButton::setBargraphLowerLimit(int limit)
2147
{
2148
    DECL_TRACER("TButton::setBargraphLowerLimit(int limit)");
2149
 
2150
    if (limit < 1 || limit > 65535)
2151
    {
2152
        MSG_ERROR("Invalid lower limit " << limit);
2153
        return false;
2154
    }
2155
 
2156
    rl = limit;
2157
    return true;
2158
}
2159
 
2160
bool TButton::setBargraphSliderColor(const string& color)
2161
{
2162
    DECL_TRACER("TButton::setBargraphSliderColor(const string& color, int inst)");
2163
 
2164
    if (!TColor::isValidAMXcolor(color))
2165
    {
2166
        MSG_PROTOCOL("Invalid color >" << color << "< ignored!");
2167
        return false;
2168
    }
2169
 
2170
    if (type == BARGRAPH && sc != color)
2171
    {
2172
        mChanged = true;
2173
        sc = color;
2174
    }
2175
    else if (type == JOYSTICK && cc != color)
2176
    {
2177
        mChanged = true;
2178
        cc = color;
2179
    }
2180
 
2181
    if (mChanged && visible)
2182
        refresh();
2183
 
2184
    return true;
2185
}
2186
 
2187
/*
2188
 * Change the bargraph slider name or joystick cursor name.
2189
 */
2190
bool TButton::setBargraphSliderName(const string& name)
2191
{
2192
    DECL_TRACER("TButton::setBargraphSliderName(const string& name)");
2193
 
2194
    if (name.empty())
2195
        return false;
2196
 
2197
    if (!gPageManager)
2198
    {
2199
        MSG_ERROR("Page manager was not initialized!");
2200
        TError::setError();
2201
        return false;
2202
    }
2203
 
2204
    if (type == BARGRAPH && !gPageManager->getSystemDraw()->existSlider(name))
2205
    {
2206
        MSG_ERROR("The slider " << name << " doesn't exist!");
2207
        return false;
2208
    }
2209
    else if (type == JOYSTICK && !gPageManager->getSystemDraw()->existCursor(name))
2210
    {
2211
        MSG_ERROR("The cursor " << name << " doesn't exist!");
2212
        return false;
2213
    }
2214
 
2215
    if ((type == BARGRAPH && name == sd) || (type == JOYSTICK && name == cd))
2216
        return true;
2217
 
2218
    mChanged = true;
2219
 
2220
    if (type == BARGRAPH)
2221
        sd = name;
2222
    else
2223
        cd = name;
2224
 
2225
    if (visible)
2226
        refresh();
2227
 
2228
    return true;
2229
}
2230
 
2231
bool TButton::setFontFileName(const string& name, int /*size*/, int instance)
2232
{
2233
    DECL_TRACER("TButton::setFontFileName(const string& name, int size)");
2234
 
2235
    if (name.empty() || !mFonts)
2236
    {
2237
#if TESTMODE == 1
2238
        setScreenDone();
2239
#endif
2240
        return false;
2241
    }
2242
 
2243
    if ((size_t)instance >= sr.size())
2244
    {
2245
#if TESTMODE == 1
2246
        setScreenDone();
2247
#endif
2248
        return false;
2249
    }
2250
 
2251
    int id = mFonts->getFontIDfromFile(name);
2252
 
2253
    if (id == -1)
2254
    {
2255
#if TESTMODE == 1
2256
        setScreenDone();
2257
#endif
2258
        return false;
2259
    }
2260
 
2261
    if (instance < 0)
2262
    {
2263
        for (size_t i = 0; i < sr.size(); ++i)
2264
        {
2265
            if (sr[i].fi != id)
2266
                mChanged = true;
2267
 
2268
            sr[i].fi = id;
2269
        }
2270
    }
2271
    else if (sr[instance].fi != id)
2272
    {
2273
        mChanged = true;
2274
        sr[instance].fi = id;
2275
    }
2276
#if TESTMODE == 1
2277
    setScreenDone();
2278
#endif
2279
    return true;
2280
}
2281
 
2282
bool TButton::setFontName(const string &name, int instance)
2283
{
2284
    DECL_TRACER("TButton::setFontName(const string &name, int instance)");
2285
 
2286
    if (name.empty() || !mFonts)
2287
    {
2288
#if TESTMODE == 1
2289
        setScreenDone();
2290
#endif
2291
        return false;
2292
    }
2293
 
2294
    if ((size_t)instance >= sr.size())
2295
    {
2296
#if TESTMODE == 1
2297
        setScreenDone();
2298
#endif
2299
        return false;
2300
    }
2301
 
2302
    int id = mFonts->getFontIDfromName(name);
2303
 
2304
    if (id == -1)
2305
    {
2306
#if TESTMODE == 1
2307
        setScreenDone();
2308
#endif
2309
        return false;
2310
    }
2311
 
2312
    if (instance < 0)
2313
    {
2314
        for (size_t i = 0; i < sr.size(); ++i)
2315
        {
2316
            if (sr[i].fi != id)
2317
                mChanged = true;
2318
 
2319
            sr[i].fi = id;
2320
        }
2321
    }
2322
    else if (sr[instance].fi != id)
2323
    {
2324
        mChanged = true;
2325
        sr[instance].fi = id;
2326
    }
2327
#if TESTMODE == 1
2328
    setScreenDone();
2329
#endif
2330
    return true;
2331
}
2332
 
2333
bool TButton::setBitmap(const string& file, int instance)
2334
{
2335
    DECL_TRACER("TButton::setBitmap(const string& file, int instance)");
2336
 
2337
    if (instance >= (int)sr.size())
2338
    {
2339
        MSG_ERROR("Invalid parameters!");
2340
        return false;
2341
    }
2342
 
2343
    int inst = instance;
2344
    int loop = 1;
2345
 
2346
    if (inst < 0)
2347
    {
2348
        loop = (int)sr.size();
2349
        inst = 0;
2350
    }
2351
 
2352
    for (int i = 0; i < loop; ++i)
2353
    {
2354
        if (sr[inst].bm == file)
2355
        {
2356
            inst++;
2357
            continue;
2358
        }
2359
 
2360
        mChanged = true;
2361
        sr[inst].bm = file;
2362
 
2363
        if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_BITMAP))
2364
        {
2365
            sk_sp<SkData> image;
2366
            SkBitmap bm;
2367
 
2368
            image = readImage(file);
2369
 
2370
            if (image)
2371
            {
2372
                DecodeDataToBitmap(image, &bm);
2373
 
2374
                if (!bm.empty())
2375
                {
2376
                    TImgCache::addImage(sr[inst].bm, bm, _BMTYPE_BITMAP);
2377
                    sr[inst].bm_width = bm.info().width();
2378
                    sr[inst].bm_height = bm.info().height();
2379
                }
2380
            }
2381
        }
2382
 
2383
        inst++;
2384
    }
2385
 
2386
    if (!createButtons(true))   // We're forcing the image to load
2387
        return false;
2388
 
2389
    return makeElement(instance);
2390
}
2391
 
2392
bool TButton::setCameleon(const string& file, int instance)
2393
{
2394
    DECL_TRACER("TButton::setCameleon(const string& file, int instance)");
2395
 
2396
    if (file.empty() || instance >= (int)sr.size())
2397
    {
2398
        MSG_ERROR("Invalid parameters!");
2399
        return false;
2400
    }
2401
 
2402
    int inst = instance;
2403
    int loop = 1;
2404
 
2405
    if (inst < 0)
2406
    {
2407
        loop = (int)sr.size();
2408
        inst = 0;
2409
    }
2410
 
2411
    for (int i = 0; i < loop; ++i)
2412
    {
2413
        if (sr[inst].mi == file)
2414
        {
2415
            inst++;
2416
            continue;
2417
        }
2418
 
2419
        mChanged = true;
2420
        sr[inst].mi = file;
2421
 
2422
        if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_CHAMELEON))
2423
        {
2424
            sk_sp<SkData> image;
2425
            SkBitmap bm;
2426
 
2427
            image = readImage(file);
2428
 
2429
            if (image)
2430
            {
2431
                DecodeDataToBitmap(image, &bm);
2432
 
2433
                if (!bm.empty())
2434
                {
2435
                    TImgCache::addImage(sr[inst].mi, bm, _BMTYPE_CHAMELEON);
2436
                    sr[inst].mi_width = bm.info().width();
2437
                    sr[inst].mi_height = bm.info().height();
2438
                }
2439
            }
2440
        }
2441
 
2442
        inst++;
2443
    }
2444
 
2445
    if (!createButtons(true))   // We're forcing the image to load
2446
        return false;
2447
 
2448
    return makeElement(instance);
2449
}
2450
 
2451
bool TButton::setInputMask(const std::string& mask)
2452
{
2453
    DECL_TRACER("TButton::setInputMask(const std::string& mask)");
2454
 
2455
    vector<char> mTable = { '0', '9', '#', 'L', '?', 'A', 'a', '&', 'C',
2456
                            '[', ']', '|', '{', '}', '<', '>', '^' };
2457
    vector<char>::iterator iter;
2458
 
2459
    for (size_t i = 0; i < mask.length(); ++i)
2460
    {
2461
        bool found = false;
2462
 
2463
        for (iter = mTable.begin(); iter != mTable.end(); ++iter)
2464
        {
2465
            if (mask[i] == *iter)
2466
            {
2467
                found = true;
2468
                break;
2469
            }
2470
        }
2471
 
2472
        if (!found)
2473
        {
2474
            MSG_WARNING("The mask letter " << mask[i] << " is invalid!");
2475
#if TESTMODE == 1
2476
            setScreenDone();
2477
#endif
2478
            return false;
2479
        }
2480
    }
2481
 
2482
    im = mask;
2483
#if TESTMODE == 1
2484
    __success = true;
2485
    setScreenDone();
2486
#endif
2487
    return true;
2488
}
2489
 
2490
void TButton::setActiveInstance(int inst)
2491
{
2492
    DECL_TRACER("TButton::setActiveInstance()");
2493
 
2494
    if (inst < 0 || (size_t)inst >= sr.size())
2495
        return;
2496
 
2497
    if (mActInstance != inst)
2498
        mChanged = true;
2499
 
2500
    mActInstance = inst;
2501
}
2502
 
2503
SUBVIEW_POSITION_t TButton::getSubViewAnchor()
2504
{
2505
    DECL_TRACER("TButton::getSubViewAnchor()");
2506
 
2507
    if (we.empty())
2508
        return SVP_CENTER;
2509
    else if (strCaseCompare(we, "l/t") == 0)
2510
        return SVP_LEFT_TOP;
2511
    else if (strCaseCompare(we, "r/b") == 0)
2512
        return SVP_RIGHT_BOTTOM;
2513
 
2514
    return SVP_CENTER;
2515
}
2516
 
2517
bool TButton::getDynamic(int inst)
2518
{
2519
    DECL_TRACER("TButton::getDynamic(int inst)");
2520
 
2521
    if (inst < 0 || inst >= (int)sr.size())
2522
    {
2523
        MSG_ERROR("Instance " << inst << " does not exist!");
2524
        return false;
2525
    }
2526
 
2527
    return sr[inst].dynamic;
2528
}
2529
 
2530
void TButton::setDynamic(int d, int inst)
2531
{
2532
    DECL_TRACER("TButton::setDynamic(int d, int inst)");
2533
 
2534
    if (inst >= (int)sr.size())
2535
    {
2536
        MSG_ERROR("Instance is out of size!");
2537
        return;
2538
    }
2539
 
2540
    bool dyn = (d != 0) ? true : false;
2541
 
2542
    if (inst < 0)
2543
    {
2544
        vector<SR_T>::iterator iter;
2545
        int instance = 0;
2546
 
2547
        for (iter = sr.begin(); iter != sr.end(); ++iter)
2548
        {
2549
            bool old = iter->dynamic;
2550
            iter->dynamic = dyn;
2551
 
2552
            if (old && old != dyn && mActInstance == instance)
2553
            {
2554
                THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
2555
 
2556
                if (thref)
2557
                {
2558
                    TImageRefresh *mImageRefresh = thref->mImageRefresh;
2559
 
2560
                    if (mImageRefresh)
2561
                        mImageRefresh->stop();
2562
                }
2563
 
2564
                mChanged = true;
2565
                makeElement(instance);
2566
            }
2567
 
2568
            instance++;
2569
        }
2570
    }
2571
    else
2572
    {
2573
        bool old = sr[inst].dynamic;
2574
        sr[inst].dynamic = dyn;
2575
 
2576
        if (old && old != dyn && mActInstance == inst)
2577
        {
2578
            THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
2579
 
2580
            if (thref)
2581
            {
2582
                TImageRefresh *mImageRefresh = thref->mImageRefresh;
2583
 
2584
                if (mImageRefresh)
2585
                    mImageRefresh->stop();
2586
            }
2587
 
2588
            mChanged = true;
2589
            makeElement(inst);
2590
        }
2591
    }
2592
}
2593
 
2594
int TButton::getOpacity(int inst)
2595
{
2596
    DECL_TRACER("TButoon::getOpacity(int inst)");
2597
 
2598
    if (inst < 0 || inst >= (int)sr.size())
2599
    {
2600
        MSG_ERROR("Instance " << inst << " does not exist!");
2601
        return 0;
2602
    }
2603
 
2604
    return sr[inst].oo;
2605
}
2606
 
2607
bool TButton::setOpacity(int op, int instance)
2608
{
2609
    DECL_TRACER("TButton::setOpacity(int op, int instance)");
2610
 
2611
    if (instance >= 0 && (size_t)instance >= sr.size())
2612
    {
2613
        MSG_ERROR("Instance " << instance << " does not exist!");
2614
#if TESTMODE == 1
2615
        setScreenDone();
2616
#endif
2617
        return false;
2618
    }
2619
 
2620
    if (op < 0 || op > 255)
2621
    {
2622
        MSG_ERROR("Invalid opacity " << op << "!");
2623
#if TESTMODE == 1
2624
        setScreenDone();
2625
#endif
2626
        return false;
2627
    }
2628
 
2629
    if (instance < 0)
2630
    {
2631
        for (size_t i = 0; i < sr.size(); ++i)
2632
        {
2633
            if (sr[i].oo == op)
2634
                continue;
2635
 
2636
            sr[i].oo = op;
2637
            mChanged = true;
2638
        }
2639
    }
2640
    else if (sr[instance].oo != op)
2641
    {
2642
        sr[instance].oo = op;
2643
        mChanged = true;
2644
    }
2645
 
2646
    if (!mChanged)
2647
    {
2648
#if TESTMODE == 1
2649
        __success = true;
2650
        setScreenDone();
2651
#endif
2652
        return true;
2653
    }
2654
 
2655
    return makeElement(instance);
2656
}
2657
 
2658
bool TButton::setFont(int id, int instance)
2659
{
2660
    DECL_TRACER("TButton::setFont(int id)");
2661
 
2662
    if (!setFontOnly(id, instance))
2663
        return false;
2664
 
2665
    return makeElement(instance);
2666
}
2667
 
2668
bool TButton::setFontOnly(int id, int instance)
2669
{
2670
    DECL_TRACER("TButton::setFontOnly(int id)");
2671
 
2672
    if (instance >= 0 && (size_t)instance >= sr.size())
2673
    {
2674
        MSG_ERROR("Instance " << instance << " does not exist!");
2675
        return false;
2676
    }
2677
 
2678
    if (instance < 0)
2679
    {
2680
        for (size_t i = 0; i < sr.size(); ++i)
2681
        {
2682
            if (sr[i].fi != id)
2683
            {
2684
                mChanged = true;
2685
                sr[i].fi = id;
2686
            }
2687
        }
2688
    }
2689
    else if (sr[instance].fi != id)
2690
    {
2691
        mChanged = true;
2692
        sr[instance].fi = id;
2693
    }
2694
 
2695
    return true;
2696
}
2697
 
2698
void TButton::setLeft(int left)
2699
{
2700
    DECL_TRACER("TButton::setLeft(int left)");
2701
 
2702
    if (left < 0)
2703
        return;
2704
 
2705
    if (mPosLeft != left)
2706
        mChanged = true;
2707
 
2708
    mPosLeft = left;
2709
    makeElement(mActInstance);
2710
}
2711
 
2712
void TButton::setTop(int top)
2713
{
2714
    DECL_TRACER("TButton::setTop(int top)");
2715
 
2716
    if (top < 0)
2717
        return;
2718
 
2719
    if (mPosTop != top)
2720
        mChanged = true;
2721
 
2722
    mPosTop = top;
2723
    makeElement(mActInstance);
2724
}
2725
 
2726
void TButton::setLeftTop(int left, int top)
2727
{
2728
    DECL_TRACER("TButton::setLeftTop(int left, int top)");
2729
 
2730
    if (top < 0 || left < 0)
2731
        return;
2732
 
2733
    if (mPosLeft != left || mPosTop != top)
2734
        mChanged = true;
2735
    else
2736
        return;
2737
 
2738
    mPosLeft = left;
2739
    mPosTop = top;
2740
    makeElement(mActInstance);
2741
}
2742
 
2743
void TButton::setRectangle(int left, int top, int right, int bottom)
2744
{
2745
    DECL_TRACER("setRectangle(int left, int top, int right, int bottom)");
2746
 
2747
    if (!gPageManager)
2748
        return;
2749
 
2750
    int screenWidth = gPageManager->getSettings()->getWidth();
2751
    int screenHeight = gPageManager->getSettings()->getHeight();
2752
    int width = right - left;
2753
    int height = bottom - top;
2754
 
2755
    if (left >= 0 && right > left && (left + width) < screenWidth)
2756
        mPosLeft = left;
2757
 
2758
    if (top >= 0 && bottom > top && (top + height) < screenHeight)
2759
        mPosTop = top;
2760
 
2761
    if (left >= 0 && right > left)
2762
        wt = width;
2763
 
2764
    if (top >= 0 && bottom > top)
2765
        ht = height;
2766
}
2767
 
2768
void TButton::getRectangle(int *left, int *top, int *height, int *width)
2769
{
2770
    DECL_TRACER("TButton::getRectangle(int *left, int *top, int *height, int *width)");
2771
 
2772
    if (left)
2773
        *left = mPosLeft;
2774
 
2775
    if (top)
2776
        *top = mPosTop;
2777
 
2778
    if (height)
2779
        *height = ht;
2780
 
2781
    if (width)
2782
        *width = wt;
2783
 
2784
}
2785
 
2786
void TButton::resetButton()
2787
{
2788
    DECL_TRACER("TButton::resetButton()");
2789
 
2790
    if (mPosLeft == lt && mPosTop == tp && wt == mWidthOrig && ht == mHeightOrig)
2791
        return;
2792
 
2793
    mChanged = true;
2794
    mPosLeft = lt;
2795
    mPosTop = tp;
2796
    wt = mWidthOrig;
2797
    ht = mHeightOrig;
2798
}
2799
 
2800
void TButton::setResourceName(const string& name, int instance)
2801
{
2802
    DECL_TRACER("TButton::setResourceName(const string& name, int instance)");
2803
 
2804
    if (instance >= (int)sr.size())
2805
    {
2806
        MSG_ERROR("Invalid instance " << instance);
2807
        return;
2808
    }
2809
 
2810
    int inst = instance;
2811
    int loop = 1;
2812
 
2813
    if (inst < 0)
2814
    {
2815
        loop = (int)sr.size();
2816
        inst = 0;
2817
    }
2818
 
2819
    for (int i = 0; i < loop; ++i)
2820
    {
2821
        if (!sr[inst].dynamic)
2822
        {
2823
            inst++;
2824
            continue;
2825
        }
2826
 
2827
        if (sr[inst].bm != name)
2828
            mChanged = true;
2829
 
2830
        sr[inst].bm = name;
2831
        inst++;
2832
    }
2833
}
2834
 
2835
int TButton::getBitmapJustification(int* x, int* y, int instance)
2836
{
2837
    DECL_TRACER("TButton::getBitmapJustification(int* x, int* y, int instance)");
2838
 
2839
    if (instance < 0 || instance >= (int)sr.size())
2840
    {
2841
        MSG_ERROR("Invalid instance " << (instance + 1));
2842
        return -1;
2843
    }
2844
 
2845
    if (x)
2846
        *x = sr[instance].jb == 0 ? sr[instance].bx : 0;
2847
 
2848
    if (y)
2849
        *y = sr[instance].jb == 0 ? sr[instance].by : 0;
2850
 
2851
    return sr[instance].jb;
2852
}
2853
 
2854
void TButton::setBitmapJustification(int j, int x, int y, int instance)
2855
{
2856
    DECL_TRACER("TButton::setBitmapJustification(int j, int instance)");
2857
 
2858
    if (j < 0 || j > 9 || instance >= (int)sr.size())
2859
    {
2860
#if TESTMODE == 1
2861
        setScreenDone();
2862
#endif
2863
        return;
2864
    }
2865
 
2866
    if (instance < 0)
2867
    {
2868
        for (size_t i = 0; i < sr.size(); i++)
2869
        {
2870
            if (sr[i].jb != j)
2871
                mChanged = true;
2872
 
2873
            sr[i].jb = j;
2874
 
2875
            if (j == 0)
2876
            {
2877
                sr[i].bx = x;
2878
                sr[i].by = y;
2879
            }
2880
        }
2881
    }
2882
    else
2883
    {
2884
        if (sr[instance].jb != j)
2885
            mChanged = true;
2886
 
2887
        sr[instance].jb = j;
2888
 
2889
        if (j == 0)
2890
        {
2891
            sr[instance].bx = x;
2892
            sr[instance].by = y;
2893
        }
2894
    }
2895
 
2896
    makeElement();
2897
}
2898
 
2899
int TButton::getIconJustification(int* x, int* y, int instance)
2900
{
2901
    DECL_TRACER("TButton::getIconJustification(int* x, int* y, int instance)");
2902
 
2903
    if (instance < 0 || instance >= (int)sr.size())
2904
    {
2905
        MSG_ERROR("Invalid instance " << (instance + 1));
2906
        return -1;
2907
    }
2908
 
2909
    if (x)
2910
        *x = sr[instance].ji == 0 ? sr[instance].ix : 0;
2911
 
2912
    if (y)
2913
        *y = sr[instance].ji == 0 ? sr[instance].iy : 0;
2914
 
2915
    return sr[instance].ji;
2916
}
2917
 
2918
void TButton::setIconJustification(int j, int x, int y, int instance)
2919
{
2920
    DECL_TRACER("TButton::setIconJustification(int j, int x, int y, int instance)");
2921
 
2922
    if (j < 0 || j > 9 || instance >= (int)sr.size())
2923
    {
2924
#if TESTMODE == 1
2925
        setScreenDone();
2926
#endif
2927
        return;
2928
    }
2929
 
2930
    if (instance < 0)
2931
    {
2932
        for (size_t i = 0; i < sr.size(); i++)
2933
        {
2934
            if (sr[i].ji != j)
2935
                mChanged = true;
2936
 
2937
            sr[i].ji = j;
2938
 
2939
            if (j == 0)
2940
            {
2941
                sr[i].ix = x;
2942
                sr[i].iy = y;
2943
            }
2944
        }
2945
    }
2946
    else
2947
    {
2948
        if (sr[instance].ji != j)
2949
            mChanged = true;
2950
 
2951
        sr[instance].ji = j;
2952
 
2953
        if (j == 0)
2954
        {
2955
            sr[instance].ix = x;
2956
            sr[instance].iy = y;
2957
        }
2958
    }
2959
 
2960
    makeElement();
2961
}
2962
 
2963
int TButton::getTextJustification(int* x, int* y, int instance)
2964
{
2965
    DECL_TRACER("TButton::getTextJustification(int* x, int* y, int instance)");
2966
 
2967
    if (instance < 0 || instance >= (int)sr.size())
2968
    {
2969
        MSG_ERROR("Invalid instance " << (instance + 1));
2970
        return -1;
2971
    }
2972
 
2973
    if (x)
2974
        *x = sr[instance].jt == 0 ? sr[instance].tx : 0;
2975
 
2976
    if (y)
2977
        *y = sr[instance].jt == 0 ? sr[instance].ty : 0;
2978
 
2979
    return sr[instance].jt;
2980
}
2981
 
2982
void TButton::setTextJustification(int j, int x, int y, int instance)
2983
{
2984
    DECL_TRACER("TButton::setTextJustification(int j, int x, int y, int instance)");
2985
 
2986
    if (!setTextJustificationOnly(j, x, y, instance))
2987
    {
2988
#if TESTMODE == 1
2989
        setScreenDone();
2990
#endif
2991
        return;
2992
    }
2993
 
2994
    makeElement();
2995
}
2996
 
2997
bool TButton::setTextJustificationOnly(int j, int x, int y, int instance)
2998
{
2999
    DECL_TRACER("TButton::setTextJustificationOnly(int j, int x, int y, int instance)");
3000
 
3001
    if (j < 0 || j > 9 || instance >= (int)sr.size())
3002
        return false;
3003
 
3004
    if (instance < 0)
3005
    {
3006
        for (size_t i = 0; i < sr.size(); i++)
3007
        {
3008
            if (sr[i].jt != j)
3009
                mChanged = true;
3010
 
479 andreas 3011
            sr[i].jt = (ORIENTATION)j;
446 andreas 3012
 
3013
            if (j == 0)
3014
            {
3015
                sr[i].tx = x;
3016
                sr[i].ty = y;
3017
            }
3018
        }
3019
    }
3020
    else
3021
    {
3022
        if (sr[instance].jt != j)
3023
            mChanged = true;
3024
 
479 andreas 3025
        sr[instance].jt = (ORIENTATION)j;
446 andreas 3026
 
3027
        if (j == 0)
3028
        {
3029
            sr[instance].tx = x;
3030
            sr[instance].ty = y;
3031
        }
3032
    }
3033
 
3034
    return true;
3035
}
3036
 
3037
string TButton::getText(int inst)
3038
{
3039
    DECL_TRACER("TButton::getText(int inst)");
3040
 
3041
    if (inst < 0 || inst >= (int)sr.size())
3042
    {
3043
        MSG_ERROR("Instance " << inst << " does not exist!");
3044
        return string();
3045
    }
3046
 
3047
    return sr[inst].te;
3048
}
3049
 
3050
string TButton::getTextColor(int inst)
3051
{
3052
    DECL_TRACER("TButton::getTextColor(int const)");
3053
 
3054
    if (inst < 0 || inst >= (int)sr.size())
3055
    {
3056
        MSG_ERROR("Instance " << inst << " does not exist!");
3057
        return string();
3058
    }
3059
 
3060
    return sr[inst].ct;
3061
}
3062
 
3063
string TButton::getTextEffectColor(int inst)
3064
{
3065
    DECL_TRACER ("TButton::getTextEffectColor(int inst)");
3066
 
3067
    if (inst < 0 || inst >= (int)sr.size())
3068
    {
3069
        MSG_ERROR("Instance " << inst << " does not exist!");
3070
        return string();
3071
    }
3072
 
3073
    return sr[inst].ec;
3074
}
3075
 
3076
void TButton::setTextEffectColor(const string& ec, int instance)
3077
{
3078
    DECL_TRACER("TButton::setTextEffectColor(const string& ec, int inst)");
3079
 
3080
    if (!setTextEffectColorOnly(ec, instance))
3081
        return;
3082
 
3083
    if (visible)
3084
        makeElement();
3085
}
3086
 
3087
bool TButton::setTextEffectColorOnly(const string& ec, int instance)
3088
{
3089
    DECL_TRACER("TButton::setTextEffectColorOnly(const string& ec, int inst)");
3090
 
3091
    if ((size_t)instance >= sr.size())
3092
    {
3093
        MSG_ERROR("Instance " << instance << " does not exist!");
3094
        return false;
3095
    }
3096
 
3097
    if (!TColor::isValidAMXcolor(ec))
3098
    {
3099
        MSG_PROTOCOL("Invalid color >" << ec << "< ignored!");
3100
        return false;
3101
    }
3102
 
3103
    int inst = instance;
3104
    int loop = 1;
3105
 
3106
    if (inst < 0)
3107
    {
3108
        loop = (int)sr.size();
3109
        inst = 0;
3110
    }
3111
 
3112
    for (int i = 0; i < loop; ++i)
3113
    {
3114
        if (sr[inst].ec.compare(ec) == 0)
3115
        {
3116
            inst++;
3117
            continue;
3118
        }
3119
 
3120
        sr[inst].ec = ec;
3121
        mChanged = true;
3122
        inst++;
3123
    }
3124
 
3125
    return true;
3126
}
3127
 
3128
int TButton::getTextEffect(int inst)
3129
{
3130
    DECL_TRACER("TButton::getTextEffect(int inst)");
3131
 
3132
    if (inst < 0 || inst >= (int)sr.size())
3133
    {
3134
        MSG_ERROR("Instance " << inst << " does not exist!");
3135
        return 0;
3136
    }
3137
 
3138
    return sr[inst].et;
3139
}
3140
 
3141
void TButton::setTextEffect(int et, int inst)
3142
{
3143
    DECL_TRACER("TButton::setTextEffect(bool et, int inst)");
3144
 
3145
    if (inst >= (int)sr.size())
3146
    {
3147
        MSG_ERROR("instance " << inst << " is out of bounds!");
3148
        return;
3149
    }
3150
 
3151
    if (inst < 0)
3152
    {
3153
        for (size_t i = 0; i < sr.size(); i++)
3154
        {
3155
            if (sr[i].et != et)
3156
                mChanged = true;
3157
 
3158
            sr[i].et = et;
3159
        }
3160
    }
3161
    else
3162
    {
3163
        if (sr[inst].et != et)
3164
            mChanged = true;
3165
 
3166
        sr[inst].et = et;
3167
    }
3168
 
3169
    makeElement();
3170
}
3171
 
3172
string TButton::getTextEffectName(int inst)
3173
{
3174
    DECL_TRACER("TButton::getTextEffectName(int inst)");
3175
 
3176
    if (inst < 0 || inst >= (int)sr.size())
3177
        return string();
3178
 
3179
    int idx = 0;
3180
 
3181
    while (sysTefs[idx].idx)
3182
    {
3183
        if (sysTefs[idx].idx == sr[inst].et)
3184
            return sysTefs[idx].name;
3185
 
3186
        idx++;
3187
    }
3188
 
3189
    return string();
3190
}
3191
 
3192
void TButton::setTextEffectName(const string& name, int inst)
3193
{
3194
    DECL_TRACER("TButton::setTextEffectName(const string& name, int inst)");
3195
 
3196
    if (inst >= (int)sr.size())
3197
        return;
3198
 
3199
    int idx = 0;
3200
 
3201
    while (sysTefs[idx].idx)
3202
    {
3203
        if (strCaseCompare(sysTefs[idx].name, name) == 0)
3204
        {
3205
            if (inst < 0)
3206
            {
3207
                for (size_t i = 0; i < sr.size(); i++)
3208
                {
3209
                    if (sr[i].et != sysTefs[idx].idx)
3210
                        mChanged = true;
3211
 
3212
                    sr[i].et = sysTefs[idx].idx;
3213
                }
3214
            }
3215
            else
3216
            {
3217
                if (sr[inst].et != sysTefs[idx].idx)
3218
                    mChanged = true;
3219
 
3220
                sr[inst].et = sysTefs[idx].idx;
3221
            }
3222
 
3223
            makeElement();
3224
            break;
3225
        }
3226
 
3227
        idx++;
3228
    }
3229
}
3230
 
3231
string TButton::getBitmapName(int inst)
3232
{
3233
    DECL_TRACER("TButton::getBitmapName(int inst)");
3234
 
3235
    if (inst < 0 || inst >= (int)sr.size())
3236
    {
3237
        MSG_ERROR("Instance " << inst << " does not exist!");
3238
        return string();
3239
    }
3240
 
3241
    return sr[inst].bm;
3242
}
3243
 
3244
string TButton::getFillColor(int inst)
3245
{
3246
    DECL_TRACER("TButton::getFillColor(int inst)");
3247
 
3248
    if (inst < 0 || inst >= (int)sr.size())
3249
    {
3250
        MSG_ERROR("Instance " << inst << " does not exist!");
3251
        return string();
3252
    }
3253
 
3254
    return sr[inst].cf;
3255
}
3256
 
3257
bool TButton::setTextWordWrap(bool state, int instance)
3258
{
3259
    DECL_TRACER("TButton::setWorWrap(bool state, int instance)");
3260
 
3261
    if (instance >= (int)sr.size())
3262
    {
3263
        MSG_ERROR("Invalid instance " << instance);
3264
        return false;
3265
    }
3266
 
3267
    int stt = state ? 1 : 0;
3268
 
3269
    if (instance < 0)
3270
    {
3271
        for (size_t i = 0; i < sr.size(); i++)
3272
        {
3273
            if (sr[i].ww != stt)
3274
                mChanged = true;
3275
 
3276
            sr[i].ww = stt;
3277
        }
3278
    }
3279
    else
3280
    {
3281
        if (sr[instance].ww != stt)
3282
            mChanged = true;
3283
 
3284
        sr[instance].ww = stt;
3285
    }
3286
 
3287
    return makeElement(instance);
3288
}
3289
 
3290
void TButton::setMarqueeSpeed(int speed, int inst)
3291
{
3292
    DECL_TRACER("TButton::setMarqueeSpeed(int speed, int inst)");
3293
 
3294
    if (inst >= (int)sr.size())
3295
    {
3296
        MSG_ERROR("Invalid instance " << inst);
3297
        return;
3298
    }
3299
 
3300
    if (speed < 1 || speed > 10)
3301
    {
3302
        MSG_ERROR("Speed for marquee line is out of range!");
3303
        return;
3304
    }
3305
 
3306
    if (inst < 0)
3307
    {
3308
        for (size_t i = 0; i < sr.size(); ++i)
3309
            sr[i].ms = speed;
3310
    }
3311
    else
3312
        sr[inst].ms = speed;
3313
}
3314
 
3315
int TButton::getMarqueeSpeed(int inst)
3316
{
3317
    DECL_TRACER("TButton::getMarqueeSpeed(int inst)");
3318
 
3319
    if (inst >= (int)sr.size())
3320
    {
3321
        MSG_ERROR("Invalid instance " << inst);
3322
        return 1;
3323
    }
3324
 
3325
    if (inst <= 0)
3326
        return sr[0].ms;
3327
    else
3328
        return sr[inst].ms;
3329
}
3330
 
3331
bool TButton::getTextWordWrap(int inst)
3332
{
3333
    DECL_TRACER("TButton::getTextWordWrap(int inst)");
3334
 
3335
    if (inst < 0 || inst >= (int)sr.size())
3336
    {
3337
        MSG_ERROR("Instance " << inst << " does not exist!");
3338
        return false;
3339
    }
3340
 
3341
    return (sr[inst].ww == 1);
3342
}
3343
 
3344
int TButton::getFontIndex(int inst)
3345
{
3346
    DECL_TRACER("TButton::getFontIndex(int inst)");
3347
 
3348
    if (inst < 0 || inst >= (int)sr.size())
3349
    {
3350
        MSG_ERROR("Instance " << inst << " does not exist!");
3351
        return 0;
3352
    }
3353
 
3354
    return sr[inst].fi;
3355
}
3356
 
3357
bool TButton::setFontIndex(int fi, int instance)
3358
{
3359
    DECL_TRACER("TButton::setFontIndex(int fi, int inst)");
3360
 
3361
    if (instance >= (int)sr.size())
3362
    {
3363
        MSG_ERROR("Invalid instance " << instance);
3364
        return false;
3365
    }
3366
 
3367
    int inst = instance;
3368
    int loop = 1;
3369
 
3370
    if (inst < 0)
3371
    {
3372
        loop = (int)sr.size();
3373
        inst = 0;
3374
    }
3375
 
3376
    for (int i = 0; i < loop; ++i)
3377
    {
3378
        if (sr[inst].fi != fi)
3379
            mChanged = true;
3380
 
3381
        sr[inst].fi = fi;
3382
        inst++;
3383
    }
3384
 
3385
    return makeElement(inst);
3386
}
3387
 
3388
int TButton::getIconIndex(int inst)
3389
{
3390
    DECL_TRACER("TButton::getIconIndex(int inst)");
3391
 
3392
    if (inst < 0 || inst >= (int)sr.size())
3393
    {
3394
        MSG_ERROR("Instance " << inst << " does not exist!");
3395
        return 0;
3396
    }
3397
 
3398
    return sr[inst].ii;
3399
}
3400
 
3401
string TButton::getSound(int inst)
3402
{
3403
    DECL_TRACER("TButton::getSound(int inst)");
3404
 
3405
    if (inst < 0 || inst >= (int)sr.size())
3406
    {
3407
        MSG_ERROR("Instance " << inst << " does not exist!");
3408
        return string();
3409
    }
3410
 
3411
    return sr[inst].sd;
3412
}
3413
 
3414
void TButton::setSound(const string& sound, int inst)
3415
{
3416
    DECL_TRACER("TButton::setSound(const string& sound, int inst)");
3417
 
3418
    if (inst >= (int)sr.size())
3419
    {
3420
        MSG_ERROR("Invalid instance " << inst);
3421
        return;
3422
    }
3423
 
3424
    if (inst < 0)
3425
    {
3426
        for (size_t i = 0; i < sr.size(); i++)
3427
            sr[i].sd = sound;
3428
    }
3429
    else
3430
        sr[inst].sd = sound;
3431
#if TESTMODE == 1
3432
    __success = true;
3433
    setScreenDone();
3434
#endif
3435
}
3436
 
3437
bool TButton::startAnimation(int st, int end, int time)
3438
{
3439
    DECL_TRACER("TButton::startAnimation(int start, int end, int time)");
3440
 
3441
    if (st > end || st < 0 || (size_t)end > sr.size() || time < 0)
3442
    {
3443
        MSG_ERROR("Invalid parameter: start=" << st << ", end=" << end << ", time=" << time);
3444
        return false;
3445
    }
3446
 
3447
    if (time <= 1)
3448
    {
3449
        int inst = end - 1;
3450
 
3451
        if (inst >= 0 && (size_t)inst < sr.size())
3452
        {
3453
            if (mActInstance != inst)
3454
            {
3455
                mActInstance = inst;
3456
                mChanged = true;
3457
                drawButton(inst);
3458
            }
3459
        }
3460
 
3461
        return true;
3462
    }
3463
 
3464
    int start = std::max(1, st);
3465
 
3466
    if (mAniRunning || mThrAni.joinable())
3467
    {
3468
        MSG_PROTOCOL("Animation is already running!");
3469
        return true;
3470
    }
3471
 
3472
    int number = end - start;
3473
    ulong stepTime = ((ulong)time * 10L) / (ulong)number;
3474
    mAniRunTime = (ulong)time * 10L;
3475
 
3476
    try
3477
    {
3478
        mAniStop = false;
3479
        mThrAni = thread([=] { runAnimationRange(start, end, stepTime); });
3480
        mThrAni.detach();
3481
    }
3482
    catch (exception& e)
3483
    {
3484
        MSG_ERROR("Error starting the button animation thread: " << e.what());
3485
        return false;
3486
    }
3487
 
3488
    return true;
3489
}
3490
 
3491
void TButton::_TimerCallback(ulong)
3492
{
3493
    mLastBlink.second++;
3494
    int months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
3495
 
3496
    if ((mLastBlink.year % 4) == 0)
3497
        months[1] = 29;
3498
 
3499
    if (mLastBlink.second > 59)
3500
    {
3501
        mLastBlink.minute++;
3502
        mLastBlink.second = 0;
3503
 
3504
        if (mLastBlink.minute > 59)
3505
        {
3506
            mLastBlink.hour++;
3507
            mLastBlink.minute = 0;
3508
 
3509
            if (mLastBlink.hour >= 24)
3510
            {
3511
                mLastBlink.hour = 0;
3512
                mLastBlink.weekday++;
3513
                mLastBlink.day++;
3514
 
3515
                if (mLastBlink.weekday > 7)
3516
                    mLastBlink.weekday = 0;
3517
 
3518
                if (mLastBlink.day > months[mLastBlink.month-1])
3519
                {
3520
                    mLastBlink.day = 1;
3521
                    mLastBlink.month++;
3522
 
3523
                    if (mLastBlink.month > 12)
3524
                    {
3525
                        mLastBlink.year++;
3526
                        mLastBlink.month = 1;
3527
                    }
3528
                }
3529
            }
3530
        }
3531
    }
3532
 
3533
    funcTimer(mLastBlink);
3534
}
3535
 
3536
void TButton::_imageRefresh(const string& url)
3537
{
3538
    DECL_TRACER("TButton::_imageRefresh(const string& url)");
3539
 
3540
    if (prg_stopped || killed || !visible)
3541
        return;
3542
 
3543
    if (!gPrjResources)
3544
    {
3545
        MSG_WARNING("No resources available!");
3546
        return;
3547
    }
3548
 
3549
    ulong parent = mHandle & 0xffff0000;
3550
    getDrawOrder(sr[mActInstance]._do, (DRAW_ORDER *)&mDOrder);
3551
 
3552
    if (TError::isError())
3553
    {
3554
        TError::clear();
3555
        return;
3556
    }
3557
 
3558
    SkBitmap imgButton;
3559
 
3560
    if (!allocPixels(wt, ht, &imgButton))
3561
        return;
3562
 
3563
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
3564
    {
3565
        if (mDOrder[i] == ORD_ELEM_FILL)
3566
        {
3567
            if (!buttonFill(&imgButton, mActInstance))
3568
                return;
3569
        }
3570
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
3571
        {
3572
            RESOURCE_T resource = gPrjResources->findResource(sr[mActInstance].bm);
3573
 
3574
            if (resource.protocol.empty())
3575
            {
3576
                MSG_ERROR("Resource " << sr[mActInstance].bm << " not found!");
3577
                return;
3578
            }
3579
 
3580
            THTTPClient *WEBClient = nullptr;
3581
 
3582
            try
3583
            {
3584
                char *content = nullptr;
3585
                size_t length = 0, contentlen = 0;
3586
 
3587
                WEBClient = new THTTPClient;
3588
 
3589
                if (WEBClient && (content = WEBClient->tcall(&length, url, resource.user, resource.password)) == nullptr)
3590
                {
3591
                    if (WEBClient)
3592
                        delete WEBClient;
3593
 
3594
                    return;
3595
                }
3596
 
3597
                contentlen = WEBClient->getContentSize();
3598
 
3599
                if (content == nullptr)
3600
                {
3601
                    MSG_ERROR("Server returned no or invalid content!");
3602
                    delete WEBClient;
3603
                    return;
3604
                }
3605
 
3606
                sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
3607
 
3608
                if (!data)
3609
                {
3610
                    MSG_ERROR("Could not make an image!");
3611
                    delete WEBClient;
3612
                    return;
3613
                }
3614
 
3615
                SkBitmap image;
3616
 
3617
                if (!DecodeDataToBitmap(data, &image))
3618
                {
3619
                    MSG_ERROR("Error creating an image!");
3620
                    delete WEBClient;
3621
                    return;
3622
                }
3623
 
3624
                loadImage(&imgButton, image, mActInstance);
3625
                delete WEBClient;
3626
            }
3627
            catch (std::exception& e)
3628
            {
3629
                if (WEBClient)
3630
                    delete WEBClient;
3631
 
3632
                MSG_ERROR(e.what());
3633
                return;
3634
            }
3635
            catch(...)
3636
            {
3637
                if (WEBClient)
3638
                    delete WEBClient;
3639
 
3640
                MSG_ERROR("Unexpected exception occured. [TButton::_imageRefresh()]");
3641
                return;
3642
            }
3643
        }
3644
        else if (mDOrder[i] == ORD_ELEM_ICON)
3645
        {
3646
            if (!buttonIcon(&imgButton, mActInstance))
3647
                return;
3648
        }
3649
        else if (mDOrder[i] == ORD_ELEM_TEXT)
3650
        {
3651
            // If this is a marquee line, don't draw the text. This will be done
3652
            // by the surface.
3653
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
3654
                continue;
3655
 
3656
            if (!buttonText(&imgButton, mActInstance))
3657
                return;
3658
        }
3659
        else if (mDOrder[i] == ORD_ELEM_BORDER)
3660
        {
3661
            if (!buttonBorder(&imgButton, mActInstance))
3662
                return;
3663
        }
3664
    }
3665
 
3666
    if (mGlobalOO >= 0 || sr[mActInstance].oo >= 0) // Take overall opacity into consideration
3667
    {
3668
        SkBitmap ooButton;
3669
        int w = imgButton.width();
3670
        int h = imgButton.height();
3671
 
3672
        if (!allocPixels(w, h, &ooButton))
3673
            return;
3674
 
3675
        SkCanvas canvas(ooButton);
3676
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
3677
        SkRegion region;
3678
        region.setRect(irect);
3679
        SkScalar oo;
3680
 
3681
        if (mGlobalOO >= 0 && sr[mActInstance].oo >= 0)
3682
        {
3683
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[mActInstance].oo);
3684
            MSG_DEBUG("Set global overal opacity to " << oo);
3685
        }
3686
        else if (sr[mActInstance].oo >= 0)
3687
        {
3688
            oo = (SkScalar)sr[mActInstance].oo;
3689
            MSG_DEBUG("Set overal opacity to " << oo);
3690
        }
3691
        else
3692
        {
3693
            oo = (SkScalar)mGlobalOO;
3694
            MSG_DEBUG("Set global overal opacity to " << oo);
3695
        }
3696
 
3697
        SkScalar alpha = 1.0 / 255.0 * oo;
3698
        MSG_DEBUG("Calculated alpha value: " << alpha);
3699
        SkPaint paint;
3700
        paint.setAlphaf(alpha);
3701
        //(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
3702
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
3703
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
3704
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
3705
        imgButton = ooButton;
3706
    }
3707
 
3708
    mLastImage = imgButton;
3709
    mChanged = false;
3710
 
3711
    if (!prg_stopped && visible && _displayButton)
3712
    {
3713
        int rwidth = wt;
3714
        int rheight = ht;
3715
        int rleft = mPosLeft;
3716
        int rtop = mPosTop;
3717
#ifdef _SCALE_SKIA_
3718
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
3719
        {
3720
            rwidth = static_cast<int>(static_cast<double>(wt) * gPageManager->getScaleFactor());
3721
            rheight = static_cast<int>(static_cast<double>(ht) * gPageManager->getScaleFactor());
3722
            rleft = static_cast<int>(static_cast<double>(mPosLeft) * gPageManager->getScaleFactor());
3723
            rtop = static_cast<int>(static_cast<double>(mPosTop) * gPageManager->getScaleFactor());
3724
 
3725
            SkPaint paint;
3726
            paint.setBlendMode(SkBlendMode::kSrc);
3727
            paint.setFilterQuality(kHigh_SkFilterQuality);
3728
            // Calculate new dimension
3729
            SkImageInfo info = imgButton.info();
3730
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
3731
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
3732
            // Create a canvas and draw new image
3733
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
3734
            imgButton.allocN32Pixels(width, height);
3735
            imgButton.eraseColor(SK_ColorTRANSPARENT);
3736
            SkCanvas can(imgButton, SkSurfaceProps());
3737
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
3738
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
3739
            rowBytes = imgButton.info().minRowBytes();
3740
            mLastImage = imgButton;
3741
        }
3742
#endif
3743
        TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
3744
        _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
3745
 
3746
        if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
3747
        {
3748
            if (gPageManager && gPageManager->getSetMarqueeText())
3749
                gPageManager->getSetMarqueeText()(this);
3750
        }
3751
    }
3752
}
3753
 
3754
void TButton::registerSystemButton()
3755
{
3756
    DECL_TRACER("TButton::registerSystemButton()");
3757
 
3758
    if (mSystemReg)
3759
        return;
3760
 
3761
    // If this is a special system button, register it to receive the state
3762
    if (ap == 0 && ad == SYSTEM_ITEM_CONNSTATE)     // Connection status?
3763
    {
3764
        MSG_TRACE("Try to register button " << na << " as connection status ...");
3765
 
3766
        if (gAmxNet)
3767
        {
3768
            gAmxNet->registerNetworkState(bind(&TButton::funcNetwork, this, std::placeholders::_1), mHandle);
3769
            mSystemReg = true;
3770
            MSG_TRACE("Button registered");
3771
        }
3772
        else
3773
            MSG_WARNING("Network class not initialized!");
3774
 
3775
    }
3776
    else if (ap == 0 && ((ad >= SYSTEM_ITEM_STANDARDTIME && ad <= SYSTEM_ITEM_TIME24) || (ad >= SYSTEM_ITEM_DATEWEEKDAY && ad <= SYSTEM_ITEM_DATEYYYYMMDD))) // time or date
3777
    {
3778
        MSG_TRACE("Try to register button " << na << " as time/date ...");
3779
 
3780
        if (gAmxNet)
3781
        {
3782
            gAmxNet->registerTimer(bind(&TButton::funcTimer, this, std::placeholders::_1), mHandle);
3783
            mSystemReg = true;
3784
            MSG_TRACE("Button registered");
3785
        }
3786
        else
3787
            MSG_WARNING("Network class not initialized!");
3788
 
3789
//        if (ad >= SYSTEM_ITEM_STANDARDTIME && ad <= SYSTEM_ITEM_TIME24 && !mTimer)
3790
        if (!mTimer)
3791
        {
3792
            mTimer = new TTimer;
3793
            mTimer->setInterval(std::chrono::milliseconds(1000));   // 1 second
3794
            mTimer->registerCallback(bind(&TButton::_TimerCallback, this, std::placeholders::_1));
3795
            mTimer->run();
3796
        }
3797
    }
3798
    else if (ap == 0 && (ad == SYSTEM_ITEM_BATTERYLEVEL || ad == SYSTEM_ITEM_BATTERYCHARGING))   // Battery status
3799
    {
3800
        if (gPageManager)
3801
        {
3802
#ifdef Q_OS_ANDROID
3803
            gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), mHandle);
3804
#endif
3805
#ifdef Q_OS_IOS
3806
            gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2), mHandle);
3807
#endif
3808
        }
3809
 
3810
        mSystemReg = true;
3811
    }
3812
    else if (lp == 0 && lv == SYSTEM_ITEM_CONNSTRENGTH)       // Network connection strength
3813
    {
3814
        if (gPageManager)
3815
            gPageManager->regCallbackNetState(bind(&TButton::funcNetworkState, this, std::placeholders::_1), mHandle);
3816
 
3817
        mSystemReg = true;
3818
    }
3819
    else if (lp == 0 && lv == SYSTEM_ITEM_SYSVOLUME)        // System volume
3820
    {
3821
        int lastLevel = TConfig::getSystemVolume();
3822
 
3823
        if (gPageManager)
3824
        {
3825
            TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
3826
 
3827
            if (buttonStates)
3828
                buttonStates->setLastLevel(lastLevel);
3829
        }
3830
 
3831
        mChanged = true;
3832
        mSystemReg = true;
3833
    }
3834
    else if (cp == 0 && type == GENERAL && ch > 0 && isSystemCheckBox(ch))
3835
    {
3836
        int inst = getButtonInstance(0, ch);
3837
 
3838
        if (inst >= 0)
3839
        {
3840
            mActInstance = inst;
3841
            mChanged = true;
3842
            mSystemReg = true;
3843
        }
3844
    }
3845
    else if (ap == 0 && ad > 0 && isSystemTextLine(ad))
3846
    {
3847
        sr[0].te = sr[1].te = fillButtonText(ad, 0);
3848
        mChanged = true;
3849
        mSystemReg = true;
3850
    }
3851
}
3852
 
3853
void TButton::addPushFunction(string& func, string& page)
3854
{
3855
    DECL_TRACER("TButton::addPushFunction(string& func, string& page)");
3856
 
3857
    vector<string> allFunc = { "Stan", "Prev", "Show", "Hide", "Togg", "ClearG", "ClearP", "ClearA" };
3858
    vector<string>::iterator iter;
3859
 
3860
    for (iter = allFunc.begin(); iter != allFunc.end(); ++iter)
3861
    {
3862
        if (strCaseCompare(*iter, func) == 0)
3863
        {
3864
            bool found = false;
3865
            vector<PUSH_FUNC_T>::iterator iterPf;
3866
 
3867
            if (pushFunc.size() > 0)
3868
            {
3869
                for (iterPf = pushFunc.begin(); iterPf != pushFunc.end(); ++iterPf)
3870
                {
3871
                    if (strCaseCompare(iterPf->pfType, func) == 0)
3872
                    {
3873
                        iterPf->pfName = page;
3874
                        found = true;
3875
                        break;
3876
                    }
3877
                }
3878
            }
3879
 
3880
            if (!found)
3881
            {
3882
                PUSH_FUNC_T pf;
3883
                pf.pfType = func;
3884
                pf.pfName = page;
3885
                pushFunc.push_back(pf);
3886
            }
3887
 
3888
            break;
3889
        }
3890
    }
3891
}
3892
 
3893
void TButton::clearPushFunction(const string& action)
3894
{
3895
    DECL_TRACER("TButton::clearPushFunction(const string& action)");
3896
 
3897
    if (pushFunc.empty())
3898
        return;
3899
 
3900
    vector<PUSH_FUNC_T>::iterator iter;
3901
 
3902
    for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
3903
    {
3904
        if (strCaseCompare(iter->pfName, action) == 0)
3905
        {
3906
            pushFunc.erase(iter);
3907
            return;
3908
        }
3909
    }
3910
}
3911
 
3912
void TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)
3913
{
3914
    DECL_TRACER("TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)");
3915
 
3916
    if (!order)
3917
        return;
3918
 
3919
    if (sdo.empty() || sdo.length() != 10)
3920
    {
3921
        *order     = ORD_ELEM_FILL;
3922
        *(order+1) = ORD_ELEM_BITMAP;
3923
        *(order+2) = ORD_ELEM_BORDER;
3924
        *(order+3) = ORD_ELEM_ICON;
479 andreas 3925
        *(order+4) = ORD_ELEM_TEXT;
446 andreas 3926
        return;
3927
    }
3928
 
3929
    int elems = (int)(sdo.length() / 2);
3930
 
3931
    for (int i = 0; i < elems; i++)
3932
    {
3933
        int e = atoi(sdo.substr(i * 2, 2).c_str());
3934
 
3935
        if (e < 1 || e > 5)
3936
        {
3937
            MSG_ERROR("Invalid draw order \"" << sdo << "\"!");
3938
            TError::setError();
3939
            return;
3940
        }
3941
 
3942
        *(order+i) = (DRAW_ORDER)e;
3943
    }
3944
}
3945
 
3946
bool TButton::buttonFill(SkBitmap* bm, int instance)
3947
{
3948
    DECL_TRACER("TButton::buttonFill(SkBitmap* bm, int instance)");
3949
 
3950
    if (!bm)
3951
    {
3952
        MSG_ERROR("Invalid bitmap!");
3953
        return false;
3954
    }
3955
 
3956
    if (instance < 0 || (size_t)instance >= sr.size())
3957
    {
3958
        MSG_ERROR("Invalid instance " << instance << " (range: " << rl << " - " << rh << " [" << sr.size() << "])");
3959
        return false;
3960
    }
3961
 
3962
    SkColor color = TColor::getSkiaColor(sr[instance].cf);
3963
    MSG_DEBUG("Fill color[" << instance << "]: " << sr[instance].cf << " (#" << std::setw(8) << std::setfill('0') << std::hex << color << ")" << std::dec << std::setfill(' ') << std::setw(1));
3964
    // We create a new bitmap and fill it with the given fill color. Then
3965
    // we put this image over the existing image "bm". In case this method is
3966
    // not the first in the draw order, it prevents the button from completely
3967
    // overwrite.
3968
    SkImageInfo info = bm->info();
3969
    SkBitmap bitmap;
3970
 
3971
    if (!allocPixels(info.width(), info.height(), &bitmap))
3972
    {
3973
        MSG_ERROR("Error allocating a bitmap with size " << info.width() << " x " << info.height() << "!");
3974
        return false;
3975
    }
3976
 
3977
    bitmap.eraseColor(color);                       // Fill the new bitmap with the fill color
3978
    SkCanvas ctx(*bm, SkSurfaceProps());            // Create a canvas
3979
    SkPaint paint;                                  // The paint "device"
3980
    paint.setBlendMode(SkBlendMode::kSrcOver);      // We're overwriting each pixel
3981
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bitmap);    // Technically we need an image. So we convert our new bitmap into an image.
3982
    ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);   // Now we put the new image over the existing one.
3983
    return true;
3984
}
3985
 
3986
bool TButton::buttonBitmap(SkBitmap* bm, int inst)
3987
{
3988
    DECL_TRACER("TButton::buttonBitmap(SkBitmap* bm, int instane)");
3989
 
3990
    if (prg_stopped || !bm)
3991
        return false;
3992
 
3993
    int instance = inst;
3994
 
3995
    if (inst < 0)
3996
        instance = 0;
3997
    else if ((size_t)inst >= sr.size())
3998
        instance = (int)(sr.size() - 1);
3999
 
4000
    /*
4001
     * Here we test if we have a cameleon image. If there is a mask (sr[].mi)
4002
     * and no frame (sr[].bs) then we have a cameleon image. A bitmap is
4003
     * optional. If there is one it will be used to draw with the mask.
4004
     * Otherwise the mask may be used as an overlay for a bitmap on another
4005
     * button below the mask.
4006
     */
4007
    if (!sr[instance].mi.empty() && sr[instance].bs.empty())       // Chameleon image?
4008
    {
4009
        MSG_DEBUG("Chameleon image consisting of mask " << sr[instance].mi << " and bitmap " << (sr[instance].bm.empty() ? "NONE" : sr[instance].bm) << " ...");
4010
 
4011
        SkBitmap bmMi;
4012
        SkBitmap bmBm;
4013
 
4014
        if (!TImgCache::getBitmap(sr[instance].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[instance].mi_width, &sr[instance].mi_height))
4015
        {
4016
            sk_sp<SkData> data = readImage(sr[instance].mi);
4017
            bool loaded = false;
4018
 
4019
            if (data)
4020
            {
4021
                DecodeDataToBitmap(data, &bmMi);
4022
 
4023
                if (!bmMi.empty())
4024
                {
4025
                    TImgCache::addImage(sr[instance].mi, bmMi, _BMTYPE_CHAMELEON);
4026
                    loaded = true;
4027
                    sr[instance].mi_width = bmMi.info().width();
4028
                    sr[instance].mi_height = bmMi.info().height();
4029
                }
4030
            }
4031
 
4032
            if(!loaded)
4033
            {
4034
                MSG_ERROR("Missing image " << sr[instance].mi << "!");
4035
                TError::setError();
4036
                return false;
4037
            }
4038
        }
4039
 
4040
        MSG_DEBUG("Chameleon image size: " << bmMi.info().width() << " x " << bmMi.info().height());
4041
        SkBitmap imgRed(bmMi);
4042
        SkBitmap imgMask;
4043
        bool haveBothImages = true;
4044
 
479 andreas 4045
        if (!sr[instance].bm.empty() || (TTPInit::getTP5() && !sr[instance].bitmaps.empty()))
446 andreas 4046
        {
479 andreas 4047
            if (TTPInit::getTP5())
446 andreas 4048
            {
480 andreas 4049
                if (!buttonBitmap5(&bmBm, instance))
4050
                    haveBothImages = false;
4051
                else
4052
                {
4053
                    sr[instance].bm_width = bm->info().width();
4054
                    sr[instance].bm_height = bm->info().height();
4055
 
4056
                    if (!imgMask.installPixels(bmBm.pixmap()))
4057
                    {
4058
                        MSG_ERROR("Error installing pixmap " << sr[instance].bm << " for chameleon image!");
4059
 
4060
                        if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
4061
                            return false;
4062
 
4063
                        imgMask.eraseColor(SK_ColorTRANSPARENT);
4064
                        haveBothImages = false;
4065
                    }
4066
                }
479 andreas 4067
            }
4068
            else
4069
            {
4070
                if (!TImgCache::getBitmap(sr[instance].bm, &bmBm, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
446 andreas 4071
                {
479 andreas 4072
                    sk_sp<SkData> data = readImage(sr[instance].bm);
4073
                    bool loaded = false;
446 andreas 4074
 
479 andreas 4075
                    if (data)
446 andreas 4076
                    {
479 andreas 4077
                        DecodeDataToBitmap(data, &bmBm);
4078
 
480 andreas 4079
                        if (!bmBm.empty())
479 andreas 4080
                        {
480 andreas 4081
                            TImgCache::addImage(sr[instance].bm, bmBm, _BMTYPE_BITMAP);
479 andreas 4082
                            loaded = true;
4083
                            sr[instance].bm_width = bmBm.info().width();
4084
                            sr[instance].bm_height = bmBm.info().height();
4085
                        }
446 andreas 4086
                    }
479 andreas 4087
 
4088
                    if (!loaded)
4089
                    {
4090
                        MSG_ERROR("Missing image " << sr[instance].bm << "!");
4091
                        TError::setError();
4092
                        return false;
4093
                    }
446 andreas 4094
                }
4095
 
479 andreas 4096
                if (!bmBm.empty())
446 andreas 4097
                {
479 andreas 4098
                    if (!imgMask.installPixels(bmBm.pixmap()))
4099
                    {
4100
                        MSG_ERROR("Error installing pixmap " << sr[instance].bm << " for chameleon image!");
4101
 
4102
                        if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
4103
                            return false;
4104
 
4105
                        imgMask.eraseColor(SK_ColorTRANSPARENT);
4106
                        haveBothImages = false;
4107
                    }
446 andreas 4108
                }
479 andreas 4109
                else
446 andreas 4110
                {
479 andreas 4111
                    MSG_WARNING("No or invalid bitmap! Ignoring bitmap for cameleon image.");
446 andreas 4112
 
4113
                    if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
4114
                        return false;
4115
 
4116
                    imgMask.eraseColor(SK_ColorTRANSPARENT);
4117
                    haveBothImages = false;
4118
                }
4119
            }
4120
        }
4121
        else
4122
            haveBothImages = false;
4123
 
4124
        MSG_DEBUG("Bitmap image size: " << bmBm.info().width() << " x " << bmBm.info().height());
4125
        MSG_DEBUG("Bitmap mask size: " << imgMask.info().width() << " x " << imgMask.info().height());
4126
        SkBitmap img = drawImageButton(imgRed, imgMask, sr[instance].mi_width, sr[instance].mi_height, TColor::getSkiaColor(sr[instance].cf), TColor::getSkiaColor(sr[instance].cb));
4127
 
4128
        if (img.empty())
4129
        {
4130
            MSG_ERROR("Error creating the cameleon image \"" << sr[instance].mi << "\" / \"" << sr[instance].bm << "\"!");
4131
            TError::setError();
4132
            return false;
4133
        }
4134
 
4135
        MSG_DEBUG("Have both images: " << (haveBothImages ? "YES" : "NO"));
4136
        SkCanvas ctx(img, SkSurfaceProps());
4137
        SkImageInfo info = img.info();
4138
        SkPaint paint;
4139
        paint.setBlendMode(SkBlendMode::kSrcOver);
4140
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgMask);
4141
        ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
4142
 
4143
        POSITION_t position = calcImagePosition(sr[instance].mi_width, sr[instance].mi_height, SC_BITMAP, instance);
4144
 
4145
        if (!position.valid)
4146
        {
4147
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
4148
            TError::setError();
4149
            return false;
4150
        }
4151
 
4152
        SkCanvas can(*bm, SkSurfaceProps());
4153
        paint.setBlendMode(SkBlendMode::kSrc);
4154
 
4155
        if (sr[instance].sb == 0)
4156
        {
4157
            if (!haveBothImages)
4158
            {
4159
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img);
4160
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
4161
 
4162
                if (!sr[instance].bm.empty())
4163
                {
4164
                    imgMask.installPixels(bmBm.pixmap());
4165
                    paint.setBlendMode(SkBlendMode::kSrcOver);
4166
                    _image = SkImages::RasterFromBitmap(imgMask);
4167
                    can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
4168
                }
4169
            }
4170
            else
4171
            {
4172
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img);
4173
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
4174
            }
4175
        }
4176
        else    // Scale to fit
4177
        {
4178
            if (!haveBothImages)
4179
            {
4180
                SkRect rect;
4181
                rect.setXYWH(0, 0, imgRed.info().width(), imgRed.info().height());
4182
                sk_sp<SkImage> im = SkImages::RasterFromBitmap(img);
4183
                can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4184
 
4185
                if (!sr[instance].bm.empty())
4186
                {
4187
                    imgMask.installPixels(bmBm.pixmap());
4188
                    rect.setXYWH(position.left, position.top, position.width, position.height);
4189
                    im = SkImages::RasterFromBitmap(imgMask);
4190
                    paint.setBlendMode(SkBlendMode::kSrcOver);
4191
                    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4192
                }
4193
            }
4194
            else
4195
            {
4196
                SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
4197
                sk_sp<SkImage> im = SkImages::RasterFromBitmap(img);
4198
                can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4199
            }
4200
        }
4201
    }
4202
    else if (!sr[instance].bm.empty())
4203
    {
4204
        MSG_TRACE("Drawing normal image " << sr[instance].bm << " ...");
4205
 
4206
        SkBitmap image;
4207
 
4208
        if (!TImgCache::getBitmap(sr[instance].bm, &image, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
4209
        {
4210
            sk_sp<SkData> data = readImage(sr[instance].bm);
4211
            bool loaded = false;
4212
 
4213
            if (data)
4214
            {
4215
                DecodeDataToBitmap(data, &image);
4216
 
4217
                if (!image.empty())
4218
                {
4219
                    TImgCache::addImage(sr[instance].mi, image, _BMTYPE_BITMAP);
4220
                    loaded = true;
4221
                    sr[instance].bm_width = image.info().width();
4222
                    sr[instance].bm_height = image.info().height();
4223
                }
4224
            }
4225
 
4226
            if (!loaded)
4227
            {
4228
                MSG_ERROR("Missing image " << sr[instance].bm << "!");
4229
                return true;        // We want the button even without an image
4230
            }
4231
        }
4232
 
4233
        if (image.empty())
4234
        {
4235
            MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
4236
            TError::setError();
4237
            return false;
4238
        }
4239
 
4240
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
4241
        POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
4242
 
4243
        if (!position.valid)
4244
        {
4245
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
4246
            TError::setError();
4247
            return false;
4248
        }
4249
 
4250
        MSG_DEBUG("Putting bitmap on top of image ...");
4251
        SkPaint paint;
4252
        paint.setBlendMode(SkBlendMode::kSrcOver);
4253
        SkCanvas can(*bm, SkSurfaceProps());
4254
 
4255
        if (sr[instance].sb == 0)   // Scale bitmap?
4256
        {                           // No, keep size
4257
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
4258
            {
4259
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
4260
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
4261
            }
4262
            else    // We need only a subset of the image
4263
            {
4264
                MSG_DEBUG("Create a subset of an image ...");
4265
 
4266
                // Create a new Info to have the size of the subset.
4267
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
4268
                size_t byteSize = info.computeMinByteSize();
4269
 
4270
                if (byteSize == 0)
4271
                {
4272
                    MSG_ERROR("Unable to calculate size of image!");
4273
                    TError::setError();
4274
                    return false;
4275
                }
4276
 
4277
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
4278
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
4279
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
4280
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
4281
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
4282
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
4283
            }
4284
        }
4285
        else    // Scale to fit
4286
        {
4287
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
4288
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
4289
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4290
        }
4291
    }
4292
    else
4293
    {
4294
        MSG_DEBUG("No bitmap defined.");
4295
    }
4296
 
4297
    return true;
4298
}
4299
 
480 andreas 4300
/**
4301
 * @brief G5: Put all images together
4302
 * The method takes all defined images, scales them and put one over the other.
4303
 * The result could be combinated with an chameleon image, if there is one.
4304
 *
4305
 * @param bm        A pointer to the bitmap where the result shuld be drawn.
4306
 *                  This pointer must not be NULL.
4307
 * @param instances The instance where the bitmaps should be taken from.
4308
 */
4309
bool TButton::buttonBitmap5(SkBitmap* bm, int instance)
4310
{
4311
    DECL_TRACER("TButton::buttonBitmap5(SkBitmap* bm, int instance)");
4312
 
4313
    if (!bm)
4314
    {
4315
        MSG_WARNING("Method parameter was NULL!");
4316
        return false;
4317
    }
4318
 
4319
    if (sr[instance].bitmaps.empty())
4320
        return true;
4321
 
4322
    vector<BITMAPS_t>::iterator iter;
4323
 
4324
    for (iter = sr[instance].bitmaps.begin(); iter != sr[instance].bitmaps.end(); ++iter)
4325
    {
4326
        SkBitmap bmBm;
4327
        int width, height;
4328
 
4329
        if (!TImgCache::getBitmap(iter->fileName, &bmBm, _BMTYPE_BITMAP, &width, &height))
4330
        {
4331
            sk_sp<SkData> data = readImage(iter->fileName);
4332
            bool loaded = false;
4333
 
4334
            if (data)
4335
            {
4336
                DecodeDataToBitmap(data, &bmBm);
4337
 
4338
                if (!bmBm.empty())
4339
                {
4340
                    TImgCache::addImage(iter->fileName, bmBm, _BMTYPE_BITMAP);
4341
                    loaded = true;
4342
                }
4343
            }
4344
 
4345
            if (!loaded)
4346
            {
4347
                MSG_ERROR("Missing image " << iter->fileName << "!");
4348
                TError::setError();
4349
                return false;
4350
            }
4351
 
4352
            width = bmBm.info().width();
4353
            height = bmBm.info().height();
4354
        }
4355
 
4356
        if (!bmBm.empty())
4357
        {
4358
            // Map bitmap
4359
            SkPaint paint;
4360
            paint.setBlendMode(SkBlendMode::kSrcOver);
4361
            SkCanvas can(*bm, SkSurfaceProps());
4362
            // Scale bitmap
4363
            if (iter->justification == ORI_SCALE_FIT || iter->justification == ORI_SCALE_ASPECT)
4364
            {
4365
                SkBitmap scaled;
4366
 
4367
                if (!allocPixels(wt, ht, &scaled))
4368
                {
4369
                    MSG_ERROR("Error allocating space for a bitmap!");
4370
                    return false;
4371
                }
4372
 
4373
                SkIRect r;
4374
                r.setSize(scaled.info().dimensions());      // Set the dimensions
4375
                scaled.erase(SK_ColorTRANSPARENT, r);       // Initialize all pixels to transparent
4376
                SkCanvas canvas(scaled, SkSurfaceProps());  // Create a canvas
4377
                SkRect rect;
4378
 
4379
                if (iter->justification == ORI_SCALE_FIT)   // Scale to fit
4380
                    rect = SkRect::MakeXYWH(0, 0, wt, ht);
4381
                else                                        // Scale but maintain aspect ratio
4382
                {
4383
                    double dbl = static_cast<double>(width) / static_cast<double>(height);
4384
 
4385
                    if (static_cast<int>(static_cast<double>(ht) * dbl) <= wt)
4386
                    {
4387
                        int w = static_cast<int>(static_cast<double>(ht) * dbl);
4388
                        rect = SkRect::MakeXYWH((wt - w) / 2, 0, w, ht);
4389
                    }
4390
                    else
4391
                    {
4392
                        int h = static_cast<int>(static_cast<double>(wt) * dbl);
4393
                        rect = SkRect::MakeXYWH(0, (ht - h) / 2, wt, h);
4394
                    }
4395
                }
4396
 
4397
                sk_sp<SkImage> im = SkImages::RasterFromBitmap(bmBm);
4398
                canvas.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4399
                bmBm = scaled;
4400
                width = bmBm.info().width();
4401
                height = bmBm.info().height();
4402
            }
4403
 
4404
            // Justify bitmap
4405
            int x, y;
4406
 
4407
            switch(iter->justification)
4408
            {
4409
                case ORI_ABSOLUT:
4410
                    x = iter->offsetX;
4411
                    y = iter->offsetY;
4412
                break;
4413
 
4414
                case ORI_BOTTOM_LEFT:
4415
                    x = 0;
4416
                    y = ht - height;
4417
                break;
4418
 
4419
                case ORI_BOTTOM_MIDDLE:
4420
                    x = (wt - width) / 2;
4421
                    y = ht - height;
4422
                break;
4423
 
4424
                case ORI_BOTTOM_RIGHT:
4425
                    x = wt - width;
4426
                    y = ht -height;
4427
                break;
4428
 
4429
                case ORI_CENTER_LEFT:
4430
                    x = 0;
4431
                    y = (ht - height) / 2;
4432
                break;
4433
 
4434
                case ORI_CENTER_MIDDLE:
4435
                    x = (wt - width) / 2;
4436
                    y = (ht - height) / 2;
4437
                break;
4438
 
4439
                case ORI_CENTER_RIGHT:
4440
                    x = wt - width;
4441
                    y = (ht - height) / 2;
4442
                break;
4443
 
4444
                case ORI_TOP_LEFT:
4445
                    x = 0;
4446
                    y = 0;
4447
                break;
4448
 
4449
                case ORI_TOP_MIDDLE:
4450
                    x = (wt - width) / 2;
4451
                    y = 0;
4452
                break;
4453
 
4454
                case ORI_TOP_RIGHT:
4455
                    x = wt - width;
4456
                    y = 0;
4457
                break;
4458
 
4459
                default:
4460
                    x = 0;
4461
                    y = 0;
4462
            }
4463
 
4464
            SkRect rect = SkRect::MakeXYWH(x, y, width, height);
4465
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(bmBm);
4466
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4467
        }
4468
        else
4469
        {
4470
            MSG_WARNING("No or invalid bitmap!");
4471
            return false;
4472
        }
4473
    }
4474
 
4475
    return true;
4476
}
4477
 
4478
 
446 andreas 4479
bool TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)
4480
{
4481
    DECL_TRACER("TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)");
4482
 
4483
    if (prg_stopped)
4484
        return false;
4485
 
4486
    if (!gPrjResources)
4487
    {
4488
        MSG_ERROR("Internal error: Global resource class not initialized!");
4489
        return false;
4490
    }
4491
 
4492
    if (instance < 0 || (size_t)instance >= sr.size())
4493
    {
4494
        MSG_ERROR("Invalid instance " << instance);
4495
        return false;
4496
    }
4497
 
4498
    if (!sr[instance].dynamic)
4499
    {
4500
        MSG_WARNING("Button " << bi << ": \"" << na << "\" is not for remote image!");
4501
        return false;
4502
    }
4503
 
4504
    if (!visible)
4505
    {
4506
        MSG_DEBUG("Dynamic button " << handleToString(mHandle) << " is invisible. Will not draw it.");
4507
        return true;
4508
    }
4509
 
4510
    MSG_DEBUG("Dynamic button " << handleToString(mHandle) << " will be drawn ...");
4511
    size_t idx = 0;
4512
 
4513
    if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
4514
    {
4515
        MSG_ERROR("There exists no image resource!");
4516
        return false;
4517
    }
4518
 
4519
    RESOURCE_T resource = gPrjResources->findResource((int)idx, sr[instance].bm);
4520
 
4521
    if (resource.protocol.empty())
4522
    {
4523
        MSG_WARNING("Resource " << sr[instance].bm << " not found!");
4524
        return true;
4525
    }
4526
 
4527
    string path = resource.path;
4528
 
4529
    if (!resource.file.empty())
4530
        path += "/" + resource.file;
4531
 
4532
    string url = THTTPClient::makeURLs(toLower(resource.protocol), resource.host, 0, path);
4533
 
4534
    if (url.empty())
4535
    {
4536
        MSG_DEBUG("No URL, no bitmap!");
4537
        return true;    // We have no image but the button still exists
4538
    }
4539
 
4540
    SkBitmap image;
4541
 
4542
    if (TImgCache::getBitmap(url, &image, _BMTYPE_URL))
4543
    {
4544
        MSG_DEBUG("Found image \"" << url << "\" in the cache. Will reuse it.");
4545
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
4546
        POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
4547
 
4548
        if (!position.valid)
4549
        {
4550
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
4551
            TError::setError();
4552
            return false;
4553
        }
4554
 
4555
        SkPaint paint;
4556
        paint.setBlendMode(SkBlendMode::kSrcOver);
4557
        SkCanvas can(*bm, SkSurfaceProps());
4558
 
4559
        if (sr[instance].sb == 0)   // Scale bitmap?
4560
        {                           // No, keep size
4561
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
4562
            {
4563
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
4564
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
4565
            }
4566
            else    // We need only a subset of the image
4567
            {
4568
                MSG_DEBUG("Create a subset of an image ...");
4569
 
4570
                // Create a new Info to have the size of the subset.
4571
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
4572
                size_t byteSize = info.computeMinByteSize();
4573
 
4574
                if (byteSize == 0)
4575
                {
4576
                    MSG_ERROR("Unable to calculate size of image!");
4577
                    TError::setError();
4578
                    return false;
4579
                }
4580
 
4581
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
4582
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
4583
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
4584
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
4585
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
4586
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
4587
            }
4588
        }
4589
        else    // Scale to fit
4590
        {
4591
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
4592
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
4593
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
4594
        }
4595
 
4596
        return true;
4597
    }
4598
 
4599
    try
4600
    {
4601
        // First me must add the credential for the image into a bitmap cache element
4602
        BITMAP_CACHE bc;
4603
        bc.top = mPosTop;
4604
        bc.left = mPosLeft;
4605
        bc.width = wt;
4606
        bc.height = ht;
4607
        bc.bi = bi;
4608
        bc.show = show;
4609
        bc.handle = getHandle();
4610
        bc.parent = getParent();
4611
        bc.bitmap = *bm;
4612
        addToBitmapCache(bc);
4613
 
4614
        if (state)
4615
            *state = true;  // Prevent the calling method from displaying the button
4616
 
4617
        MSG_TRACE("Starting thread for loading a dynamic image ...");
4618
        mThrRes = std::thread([=] { this->funcResource(&resource, url, bc, instance); });
4619
        MSG_TRACE("Thread started. Detaching ...");
4620
        mThrRes.detach();
4621
        MSG_TRACE("Thread is running and detached.");
4622
    }
4623
    catch (std::exception& e)
4624
    {
4625
        MSG_ERROR("Error starting the resource thread: " << e.what());
4626
    }
4627
 
4628
    return true;
4629
}
4630
 
4631
/*
4632
 * Draws the elements of a button starting at the point where the bitmap was
4633
 * already drawed. Everything coming afterwards acording to the draw order
4634
 * is drawed in the desired order.
4635
 * This method is called out of a thread to draw a button with an external
4636
 * image coming from a WEB server.
4637
 */
4638
bool TButton::drawAlongOrder(SkBitmap *imgButton, int instance)
4639
{
4640
    DECL_TRACER("TButton::drawAlongOrder(SkBitmap *imgButton, int instance)");
4641
 
4642
    if (instance < 0 || (size_t)instance >= sr.size())
4643
    {
4644
        MSG_ERROR("Invalid instance " << instance);
4645
        return false;
4646
    }
4647
 
4648
    bool cont = false;
4649
 
4650
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
4651
    {
4652
        if (!cont && mDOrder[i] == ORD_ELEM_BITMAP)
4653
        {
4654
            cont = true;
4655
            continue;
4656
        }
4657
        else if (!cont)
4658
            continue;
4659
 
4660
        if (mDOrder[i] == ORD_ELEM_FILL)
4661
        {
4662
            if (!buttonFill(imgButton, instance))
4663
                return false;
4664
        }
4665
        else if (mDOrder[i] == ORD_ELEM_ICON)
4666
        {
4667
            if (!buttonIcon(imgButton, instance))
4668
                return false;
4669
        }
4670
        else if (mDOrder[i] == ORD_ELEM_TEXT)
4671
        {
4672
            // If this is a marquee line, don't draw the text. This will be done
4673
            // by the surface.
4674
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
4675
                continue;
4676
 
4677
            if (!buttonText(imgButton, instance))
4678
                return false;
4679
        }
4680
        else if (mDOrder[i] == ORD_ELEM_BORDER)
4681
        {
4682
            if (!buttonBorder(imgButton, instance))
4683
                return false;
4684
        }
4685
    }
4686
 
4687
    return true;
4688
}
4689
 
4690
void TButton::funcResource(const RESOURCE_T* resource, const std::string& url, BITMAP_CACHE bc, int instance)
4691
{
4692
    DECL_TRACER("TButton::funcResource(RESOURCE_T* resource, std::string& url, SkBitmap* bm, int instance)");
4693
 
4694
    if (prg_stopped || killed || _restart_ || !resource)
4695
        return;
4696
 
4697
    if (resource->refresh > 0 && !resource->dynamo)      // Periodically refreshing image?
4698
    {
4699
        MSG_DEBUG("Retrieving periodicaly refreshed image");
4700
 
4701
        if (!bc.handle || !bc.parent || bc.bi <= 1)
4702
        {
4703
            MSG_ERROR("Invalid button. Can't make a dynamo image!");
4704
            return;
4705
        }
4706
 
4707
        THR_REFRESH_t *thref = _findResource(bc.handle, bc.parent, bc.bi);
4708
        TImageRefresh *mImageRefresh = nullptr;
4709
 
4710
        if (!thref)
4711
        {
4712
            MSG_DEBUG("Creating a new refresh thread");
4713
            mImageRefresh = new TImageRefresh();
4714
            mImageRefresh->registerCallback(bind(&TButton::_imageRefresh, this, std::placeholders::_1));
4715
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
4716
            mImageRefresh->setUsername(resource->user);
4717
            mImageRefresh->setPassword(resource->password);
4718
 
4719
            if (resource->preserve)
4720
                mImageRefresh->setRunOnce();
4721
 
4722
            _addResource(mImageRefresh, bc.handle, bc.parent, bc.bi);
4723
        }
4724
        else
4725
        {
4726
            mImageRefresh = thref->mImageRefresh;
4727
 
4728
            if (!mImageRefresh)
4729
            {
4730
                MSG_ERROR("Error creating a new refresh class!");
4731
                return;
4732
            }
4733
 
4734
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
4735
            mImageRefresh->setUsername(resource->user);
4736
            mImageRefresh->setPassword(resource->password);
4737
 
4738
            if (resource->preserve)
4739
                mImageRefresh->setRunOnce();
4740
        }
4741
 
4742
        if (mImageRefresh->isRunning())
4743
            mImageRefresh->stopWait();
4744
 
4745
        if (!mImageRefresh->isRunning() && !_restart_)
4746
        {
4747
            MSG_DEBUG("Starting a refresh thread.");
4748
            mImageRefresh->run(url);
4749
        }
4750
    }
4751
    else if (resource->refresh == 0 && !resource->dynamo)
4752
    {
4753
        MSG_DEBUG("Retrieving single image");
4754
 
4755
        if (bc.handle == 0)
4756
        {
4757
            MSG_ERROR("Invalid bitmap cache!");
4758
            return;
4759
        }
4760
 
4761
        if (instance < 0 || (size_t)instance >= sr.size())
4762
        {
4763
            MSG_ERROR("Invalid instance " << instance);
4764
            return;
4765
        }
4766
 
4767
        // Check whether we have this image already
4768
        SkBitmap bitm;
4769
        bool cached = false;
4770
 
4771
        cached = TImgCache::getBitmap(url, &bitm, _BMTYPE_URL);
4772
        BITMAP_CACHE bmCache = getBCentryByHandle(bc.handle, bc.parent);
4773
 
4774
        if (!cached)    // If the bitmap was not in cache we must load it
4775
        {
4776
            MSG_DEBUG("Image not in cache. Downloading it ...");
4777
            THTTPClient *WEBClient = nullptr;
4778
 
4779
            if (bmCache.handle == 0)
4780
            {
4781
                MSG_ERROR("Couldn't find the handle " << handleToString(bc.handle) << " in bitmap cache!");
4782
                return;
4783
            }
4784
 
4785
            char *content = nullptr;
4786
            size_t length = 0, contentlen = 0;
4787
            WEBClient = new THTTPClient;
4788
 
4789
            if (!WEBClient || (content = WEBClient->tcall(&length, url, resource->user, resource->password)) == nullptr)
4790
            {
4791
                if (WEBClient)
4792
                    delete WEBClient;
4793
 
4794
                if (bc.show)
4795
                {
4796
                    setReady(bmCache.handle);
4797
                    showBitmapCache();
4798
                }
4799
                else
4800
                    setInvalid(bc.handle);
4801
 
4802
                return;
4803
            }
4804
 
4805
            contentlen = WEBClient->getContentSize();
4806
            MSG_DEBUG("Loaded " << contentlen << " bytes:");
4807
            sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
4808
 
4809
            if (!data || _restart_)
4810
            {
4811
                delete WEBClient;
4812
                MSG_ERROR("Error making image data!");
4813
 
4814
                if (bc.show)
4815
                {
4816
                    setReady(bmCache.handle);
4817
                    showBitmapCache();
4818
                }
4819
                else
4820
                    setInvalid(bc.handle);
4821
 
4822
                return;
4823
            }
4824
 
4825
            SkBitmap image;
4826
 
4827
            if (!DecodeDataToBitmap(data, &image))
4828
            {
4829
                delete WEBClient;
4830
                MSG_ERROR("Error creating an image!");
4831
 
4832
                if (bc.show)
4833
                {
4834
                    setReady(bmCache.handle);
4835
                    showBitmapCache();
4836
                }
4837
                else
4838
                    setInvalid(bc.handle);
4839
 
4840
                return;
4841
            }
4842
 
4843
            // Put this image into the static image cache
4844
            TImgCache::addImage(url, image, _BMTYPE_URL);
4845
            // Make the button complete
4846
            loadImage(&bmCache.bitmap, image, instance);
4847
            drawAlongOrder(&bmCache.bitmap, instance);
4848
            setBCBitmap(bmCache.handle, bmCache.bitmap);
4849
            setReady(bmCache.handle);
4850
            delete WEBClient;
4851
            // Display the image
4852
            showBitmapCache();
4853
            return;
4854
        }
4855
        else
4856
        {
4857
            MSG_DEBUG("Found image in cache. Using it ...");
4858
 
4859
            if (instance < 0 || (size_t)instance >= sr.size())
4860
            {
4861
                MSG_ERROR("Invalid instance " << instance);
4862
                return;
4863
            }
4864
 
4865
            loadImage(&bmCache.bitmap, bitm, instance);
4866
            setInvalid(bc.handle);
4867
 
4868
            if (bc.show && _displayButton)
4869
            {
4870
                TBitmap image((unsigned char *)bmCache.bitmap.getPixels(), bmCache.bitmap.info().width(), bmCache.bitmap.info().height());
4871
                _displayButton(bc.handle, bc.parent, image, bc.width, bc.height, bc.left, bc.top, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
4872
                mChanged = false;
4873
            }
4874
        }
4875
    }
4876
    else if (!_restart_)
4877
    {
4878
        MSG_DEBUG("Retrieving a video");
4879
 
4880
        if (_playVideo && !prg_stopped)
4881
        {
4882
            ulong parent = (mHandle >> 16) & 0x0000ffff;
4883
            _playVideo(mHandle, parent, mPosLeft, mPosTop, wt, ht, url, resource->user, resource->password);
4884
        }
4885
    }
4886
}
4887
#ifdef Q_OS_ANDROID
4888
void TButton::funcBattery(int level, bool charging, int /* chargeType */)
4889
{
4890
    DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
4891
 
4892
    // Battery level is always a bargraph
4893
    if (ap == 0 && ad == SYSTEM_ITEM_BATTERYLEVEL)       // Not charging
4894
    {
4895
        mEnabled = !charging;
4896
        mChanged = true;
4897
 
4898
        if (!mEnabled && visible)
4899
            hide(true);
4900
        else if (mEnabled)
4901
        {
4902
            visible = true;
4903
            drawBargraph(mActInstance, level, visible);
4904
        }
4905
    }
4906
    else if (ap == 0 && ad == SYSTEM_ITEM_BATTERYCHARGING)  // Charging
4907
    {
4908
        mEnabled = charging;
4909
        mChanged = true;
4910
 
4911
        if (!mEnabled && visible)
4912
            hide(true);
4913
        else if (mEnabled)
4914
        {
4915
            visible = true;
4916
            drawBargraph(mActInstance, level, visible);
4917
        }
4918
    }
4919
}
4920
#endif
4921
#ifdef Q_OS_IOS
4922
void TButton::funcBattery(int level, int state)
4923
{
4924
    DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
4925
 
4926
    // Battery level is always a bargraph
4927
    if (ap == 0 && ad == SYSTEM_ITEM_BATTERYLEVEL)       // Not charging
4928
    {
4929
        mEnabled = (state == 1 || state == 3);
4930
        mChanged = true;
4931
 
4932
        if (!mEnabled && visible)
4933
            hide(true);
4934
        else if (mEnabled)
4935
        {
4936
            visible = true;
4937
            drawBargraph(mActInstance, level, visible);
4938
        }
4939
    }
4940
    else if (ap == 0 && ad == SYSTEM_ITEM_BATTERYCHARGING)  // Charging
4941
    {
4942
        mEnabled = (state == 2);
4943
        mChanged = true;
4944
 
4945
        if (!mEnabled && visible)
4946
            hide(true);
4947
        else if (mEnabled)
4948
        {
4949
            visible = true;
4950
            drawBargraph(mActInstance, level, visible);
4951
        }
4952
    }
4953
}
4954
#endif
4955
void TButton::funcNetworkState(int level)
4956
{
4957
    DECL_TRACER("TButton::funcNetworkState(int level)");
4958
 
4959
    if (level >= rl && level <= rh)
4960
    {
4961
        int lastLevel = level;
4962
 
4963
        if (gPageManager)
4964
        {
4965
            TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
4966
 
4967
            if (buttonStates)
4968
                buttonStates->setLastLevel(level);
4969
            else
4970
                MSG_ERROR("Button states not found!");
4971
        }
4972
 
4973
        mChanged = true;
4974
        drawMultistateBargraph(lastLevel);
4975
    }
4976
}
4977
 
4978
bool TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)
4979
{
4980
    DECL_TRACER("TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)");
4981
 
4982
    if (!bm)
4983
    {
4984
        MSG_WARNING("Got no image to load!");
4985
        return false;
4986
    }
4987
 
4988
    if (instance < 0 || (size_t)instance >= sr.size())
4989
    {
4990
        MSG_ERROR("Invalid instance " << instance);
4991
        return false;
4992
    }
4993
 
4994
    SkImageInfo info = image.info();
4995
    IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
4996
    POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : info.width()), (sr[instance].sb ? isize.height : info.height()), SC_BITMAP, instance);
4997
//    POSITION_t position = calcImagePosition(info.width(), info.height(), SC_BITMAP, instance);
4998
 
4999
    if (!position.valid)
5000
    {
5001
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
5002
        return false;
5003
    }
5004
 
5005
    MSG_DEBUG("New image position: left=" << position.left << ", top=" << position.top << ", width=" << position.width << ", height=" << position.height);
5006
    MSG_DEBUG("Image size : width=" << info.width() << ", height=" << info.height());
5007
    MSG_DEBUG("Bitmap size: width=" << bm->info().width() << ", height=" << bm->info().height());
5008
    MSG_DEBUG("Putting bitmap on top of image ...");
5009
    SkPaint paint;
5010
    paint.setBlendMode(SkBlendMode::kSrc);
5011
 
5012
    SkCanvas can(*bm, SkSurfaceProps());
5013
 
5014
    if (sr[instance].sb == 0)
5015
    {
5016
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
5017
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
5018
    }
5019
    else    // Scale to fit
5020
    {
5021
//        paint.setFilterQuality(kHigh_SkFilterQuality);
5022
        SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
5023
        sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
5024
        can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
5025
    }
5026
 
5027
    return true;
5028
}
5029
 
5030
bool TButton::barLevel(SkBitmap* bm, int, int level)
5031
{
5032
    DECL_TRACER("TButton::barLevel(SkBitmap* bm, int inst, int level)");
5033
 
5034
    if (!sr[0].mi.empty() && sr[0].bs.empty() && !sr[1].bm.empty())       // Chameleon image?
5035
    {
5036
        MSG_TRACE("Chameleon image ...");
5037
        SkBitmap bmMi, bmBm;
5038
 
5039
        TImgCache::getBitmap(sr[0].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[0].mi_width, &sr[0].mi_height);
5040
        TImgCache::getBitmap(sr[1].bm, &bmBm, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);
5041
        SkBitmap imgRed(bmMi);
5042
        SkBitmap imgMask(bmBm);
5043
 
5044
        SkBitmap img;
5045
        SkPixmap pixmapRed = imgRed.pixmap();
5046
        SkPixmap pixmapMask;
5047
 
5048
        if (!imgMask.empty())
5049
            pixmapMask = imgMask.pixmap();
5050
 
5051
        int width = sr[0].mi_width;
5052
        int height = sr[0].mi_height;
5053
        int startX = 0;
5054
        int startY = 0;
5055
        // Calculation: width / <effective pixels> * level
5056
        // Calculation: height / <effective pixels> * level
5057
        if (dr.compare("horizontal") == 0)
5058
            width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
5059
        else
5060
        {
5061
            height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
5062
            startY = sr[0].mi_height - height;
5063
            height = sr[0].mi_height;
5064
        }
5065
 
5066
        if (!allocPixels(sr[0].mi_width, sr[0].mi_height, &img))
5067
            return false;
5068
 
5069
        SkCanvas canvas(img);
5070
        SkColor col1 = TColor::getSkiaColor(sr[1].cf);
5071
        SkColor col2 = TColor::getSkiaColor(sr[1].cb);
5072
 
5073
        for (int ix = 0; ix < sr[0].mi_width; ix++)
5074
        {
5075
            for (int iy = 0; iy < sr[0].mi_height; iy++)
5076
            {
5077
                SkPaint paint;
5078
                SkColor pixel;
5079
 
5080
                if (ix >= startX && ix < width && iy >= startY && iy < height)
5081
                {
5082
                    SkColor pixelRed = pixmapRed.getColor(ix, iy);
5083
                    SkColor pixelMask;
5084
 
5085
                    if (!imgMask.empty())
5086
                        pixelMask = pixmapMask.getColor(ix, iy);
5087
                    else
5088
                        pixelMask = SK_ColorWHITE;
5089
 
5090
                    pixel = baseColor(pixelRed, pixelMask, col1, col2);
5091
                }
5092
                else
5093
                    pixel = SK_ColorTRANSPARENT;
5094
 
5095
                paint.setColor(pixel);
5096
                canvas.drawPoint(ix, iy, paint);
5097
            }
5098
        }
5099
 
5100
        if (img.empty())
5101
        {
5102
            MSG_ERROR("Error creating the cameleon image \"" << sr[0].mi << "\" / \"" << sr[0].bm << "\"!");
5103
            TError::setError();
5104
            return false;
5105
        }
5106
 
5107
        SkCanvas ctx(img, SkSurfaceProps());
5108
        SkPaint paint;
5109
        paint.setBlendMode(SkBlendMode::kSrcATop);
5110
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgMask);
5111
        ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
5112
 
5113
        POSITION_t position = calcImagePosition(sr[0].mi_width, sr[0].mi_height, SC_BITMAP, 0);
5114
 
5115
        if (!position.valid)
5116
        {
5117
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
5118
            TError::setError();
5119
            return false;
5120
        }
5121
 
5122
        SkCanvas can(*bm, SkSurfaceProps());
5123
        paint.setBlendMode(SkBlendMode::kSrc);
5124
        _image = SkImages::RasterFromBitmap(img);
5125
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
5126
    }
5127
    else if (!sr[0].bm.empty() && !sr[1].bm.empty())
5128
    {
5129
        MSG_TRACE("Drawing normal image ...");
5130
        SkBitmap image1, image2;
5131
 
5132
        TImgCache::getBitmap(sr[0].bm, &image1, _BMTYPE_BITMAP, &sr[0].bm_width, &sr[0].bm_height);   // State when level = 0%
5133
        TImgCache::getBitmap(sr[1].bm, &image2, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);   // State when level = 100%
5134
        SkCanvas can_bm(*bm, SkSurfaceProps());
5135
 
5136
        if (image1.empty())
5137
        {
5138
            MSG_ERROR("Error creating the image \"" << sr[0].bm << "\"!");
5139
            TError::setError();
5140
            return false;
5141
        }
5142
 
5143
        if (image2.empty())
5144
        {
5145
            MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
5146
            TError::setError();
5147
            return false;
5148
        }
5149
 
5150
        int width = sr[1].bm_width;
5151
        int height = sr[1].bm_height;
5152
        int startX = 0;
5153
        int startY = 0;
5154
        MSG_DEBUG("Image size: " << width << " x " << height);
5155
 
5156
        // Calculation: width / <effective pixels> * level
5157
        // Calculation: height / <effective pixels> * level
5158
        if (dr.compare("horizontal") == 0)
5159
            width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
5160
        else
5161
        {
5162
            height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
5163
            startY = sr[0].bm_height - height;
5164
            height = sr[0].bm_height;
5165
        }
5166
 
5167
        MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
5168
        MSG_TRACE("Creating bargraph ...");
5169
        SkBitmap img_bar;
5170
 
5171
        if (!allocPixels(sr[1].bm_width, sr[1].bm_height, &img_bar))
5172
            return false;
5173
 
5174
        img_bar.eraseColor(SK_ColorTRANSPARENT);
5175
        SkCanvas bar(img_bar, SkSurfaceProps());
5176
 
5177
        for (int ix = 0; ix < sr[1].bm_width; ix++)
5178
        {
5179
            for (int iy = 0; iy < sr[1].bm_height; iy++)
5180
            {
5181
                SkPaint paint;
5182
                SkColor pixel;
5183
 
5184
                if (ix >= startX && ix < width && iy >= startY && iy < height)
5185
                    pixel = image2.getColor(ix, iy);
5186
                else
5187
                    pixel = SK_ColorTRANSPARENT;
5188
 
5189
                paint.setColor(pixel);
5190
                bar.drawPoint(ix, iy, paint);
5191
            }
5192
        }
5193
 
5194
        POINT_t point = getImagePosition(sr[0].bm_width, sr[0].bm_height);
5195
        SkPaint paint;
5196
        paint.setBlendMode(SkBlendMode::kSrc);
5197
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image1);
5198
        can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint);
5199
        paint.setBlendMode(SkBlendMode::kSrcATop);
5200
        _image = SkImages::RasterFromBitmap(img_bar);
5201
        can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint);       // Draw the above created image over the 0% image
5202
    }
5203
    else if (sr[0].bm.empty() && !sr[1].bm.empty())     // Only one bitmap in the second instance
5204
    {
5205
        MSG_TRACE("Drawing second image " << sr[1].bm << " ...");
5206
        SkBitmap image;
5207
        TImgCache::getBitmap(sr[1].bm, &image, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);   // State when level = 100%
5208
        SkCanvas can_bm(*bm, SkSurfaceProps());
5209
 
5210
        if (image.empty())
5211
        {
5212
            MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
5213
            TError::setError();
5214
            return false;
5215
        }
5216
 
5217
        int width = sr[1].bm_width;
5218
        int height = sr[1].bm_height;
5219
        int startX = 0;
5220
        int startY = 0;
5221
 
5222
        // Calculation: width / <effective pixels> * level
5223
        // Calculation: height / <effective pixels> * level
5224
        if (dr.compare("horizontal") == 0)
5225
            width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
5226
        else
5227
        {
5228
            height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
5229
            startY = sr[0].bm_height - height;
5230
            height = sr[0].bm_height;
5231
        }
5232
 
5233
        MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
5234
        MSG_TRACE("Creating bargraph ...");
5235
        SkBitmap img_bar;
5236
 
5237
        if (!allocPixels(sr[1].bm_width, sr[1].bm_height, &img_bar))
5238
            return false;
5239
 
5240
        img_bar.eraseColor(SK_ColorTRANSPARENT);
5241
        SkCanvas bar(img_bar, SkSurfaceProps());
5242
        SkPaint pt;
5243
 
5244
        for (int ix = 0; ix < sr[1].bm_width; ix++)
5245
        {
5246
            for (int iy = 0; iy < sr[1].bm_height; iy++)
5247
            {
5248
                SkColor pixel;
5249
 
5250
                if (ix >= startX && ix < width && iy >= startY && iy < height)
5251
                    pixel = image.getColor(ix, iy);
5252
                else
5253
                    pixel = SK_ColorTRANSPARENT;
5254
 
5255
                pt.setColor(pixel);
5256
                bar.drawPoint(ix, iy, pt);
5257
            }
5258
        }
5259
 
5260
        POINT_t point = getImagePosition(sr[1].bm_width, sr[1].bm_height);
5261
        SkPaint paint;
5262
        paint.setBlendMode(SkBlendMode::kSrcOver);
5263
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img_bar);
5264
        can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint);      // Draw the above created image over the 0% image
5265
    }
5266
    else
5267
    {
5268
        MSG_TRACE("No bitmap defined.");
5269
        int width = wt;
5270
        int height = ht;
5271
        int startX = 0;
5272
        int startY = 0;
5273
 
5274
        // Calculation: width / <effective pixels> * level = <level position>
5275
        // Calculation: height / <effective pixels> * level = <level position>
5276
        if (dr.compare("horizontal") == 0)
5277
            width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
5278
        else
5279
        {
5280
            height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
5281
            startY = ht - height;
5282
            height = ht;
5283
        }
5284
 
5285
        SkPaint paint;
5286
        paint.setBlendMode(SkBlendMode::kSrc);
5287
        SkCanvas can(*bm, SkSurfaceProps());
5288
        paint.setStyle(SkPaint::kFill_Style);
5289
        paint.setAntiAlias(true);
5290
        paint.setStrokeWidth(4);
5291
        paint.setColor(TColor::getSkiaColor(sr[1].cf));
5292
        MSG_DEBUG("Drawing rectangle: X=" << startX << ", Y=" << startY << ", W=" << width << ", H=" << height << ", level=" << level);
5293
        SkRect dst;
5294
        dst.setXYWH(startX, startY, width, height);
5295
        can.drawRect(dst, paint);
5296
        // If we have a slider button defined, we must draw it. To do it, we
5297
        // must look into the system resources to find the credentials to draw
5298
        // the button.
5299
        if (!sd.empty())
5300
        {
5301
            MSG_DEBUG("Attempt to draw the slider button \"" << sd << "\".");
5302
            int innerW = 0;
5303
            int innerH = 0;
5304
 
5305
            SkBitmap slButton = drawSliderButton(sd, TColor::getSkiaColor(sc));
5306
 
5307
            if (slButton.empty())
5308
            {
5309
                MSG_ERROR("Error drawing the slicer button " << sd);
5310
                return true;
5311
            }
5312
 
5313
            double scaleW, scaleH;
5314
            int border_size = getBorderSize(sr[0].bs);
5315
 
5316
            if (dr.compare("horizontal") != 0)
5317
            {
5318
                double scale;
5319
                innerH = static_cast<int>(static_cast<double>(height - border_size * 2 - slButton.info().height() / 2) / static_cast<double>(rh - rl) * static_cast<double>(level)) + border_size + slButton.info().height() / 2;
5320
                innerW = width;
5321
                scale = static_cast<double>(wt - border_size * 2) / static_cast<double>(slButton.info().width());
5322
                scaleW = scale;
5323
                scaleH = 1.0;
5324
                innerH = height - innerH;
5325
            }
5326
            else
5327
            {
5328
                double scale;
5329
                scale = static_cast<double>(ht - border_size * 2) / static_cast<double>(slButton.info().height());
5330
                scaleW = 1.0;
5331
                scaleH = scale;
5332
                innerH = height;
5333
                innerW = width;
5334
            }
5335
 
5336
            if (scaleImage(&slButton, scaleW, scaleH))
5337
            {
5338
                int w = slButton.info().width();
5339
                int h = slButton.info().height();
5340
 
5341
                if (dr.compare("horizontal") == 0)
5342
                {
5343
                    int pos = innerW;
5344
                    dst.setXYWH(pos - w / 2, border_size, w, h);
5345
                }
5346
                else
5347
                {
5348
                    int pos = innerH;
5349
                    dst.setXYWH(border_size, pos - h / 2, w, h);
5350
                }
5351
 
5352
                SkPaint pnt;
5353
                pnt.setBlendMode(SkBlendMode::kSrcOver);
5354
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(slButton);
5355
                can.drawImageRect(_image, dst, SkSamplingOptions(), &pnt);
5356
            }
5357
        }
5358
    }
5359
 
5360
    return true;
5361
}
5362
 
5363
POINT_t TButton::getImagePosition(int width, int height)
5364
{
5365
    DECL_TRACER("TButton::getImagePosition(int width, int height)");
5366
 
5367
    POINT_t point;
5368
 
5369
    switch (sr[0].jb)
5370
    {
5371
        case ORI_ABSOLUT:
5372
            point.x = sr[0].bx;
5373
            point.y = ht - sr[0].by;
5374
        break;
5375
 
5376
        case ORI_TOP_LEFT:
5377
            point.x = 0;
5378
            point.y = 0;
5379
        break;
5380
 
5381
        case ORI_TOP_MIDDLE:
5382
            point.x = (wt - width) / 2;
5383
            point.y = 0;
5384
        break;
5385
 
5386
        case ORI_TOP_RIGHT:
5387
            point.x = wt - width;
5388
            point.y = 0;
5389
        break;
5390
 
5391
        case ORI_CENTER_LEFT:
5392
            point.x = 0;
5393
            point.y = (ht - height) / 2;
5394
        break;
5395
 
5396
        case ORI_CENTER_MIDDLE:
5397
            point.x = (wt - width) / 2;
5398
            point.y = (ht - height) / 2;
5399
        break;
5400
 
5401
        case ORI_CENTER_RIGHT:
5402
            point.x = wt - width;
5403
            point.y = (ht - height) / 2;
5404
        break;
5405
 
5406
        case ORI_BOTTOM_LEFT:
5407
            point.x = 0;
5408
            point.y = ht - height;
5409
        break;
5410
 
5411
        case ORI_BOTTOM_MIDDLE:
5412
            point.x = (wt - width) / 2;
5413
            point.y = ht - height;
5414
        break;
5415
 
5416
        case ORI_BOTTOM_RIGHT:
5417
            point.x = wt - width;
5418
            point.y = ht - height;
5419
        break;
5420
    }
5421
 
5422
    return point;
5423
}
5424
 
5425
SkBitmap TButton::drawSliderButton(const string& slider, SkColor col)
5426
{
5427
    DECL_TRACER("TButton::drawSliderButton(const string& slider)");
5428
 
5429
    SkBitmap slButton;
5430
    // First we look for the slider button.
5431
    if (!gPageManager || !gPageManager->getSystemDraw()->existSlider(slider))
5432
        return slButton;
5433
 
5434
    // There exists one with the wanted name. We grab it and create
5435
    // the images from the files.
5436
    SLIDER_STYLE_t sst;
5437
 
5438
    if (!gPageManager->getSystemDraw()->getSlider(slider, &sst))    // should never be true!
5439
    {
5440
        MSG_ERROR("No slider entry found!");
5441
        return slButton;
5442
    }
5443
 
5444
    int width, height;
5445
 
5446
    if (dr.compare("horizontal") != 0)
5447
    {
5448
        width = (sst.fixedSize / 2) * 2 + sst.fixedSize;
5449
        height = sst.fixedSize;
5450
    }
5451
    else
5452
    {
5453
        width = sst.fixedSize;
5454
        height = (sst.fixedSize / 2) * 2 + sst.fixedSize;
5455
    }
5456
 
5457
    // Retrieve all available slider graphics files from the system
5458
    vector<SLIDER_t> sltList = gPageManager->getSystemDraw()->getSliderFiles(slider);
5459
 
5460
    if (sltList.empty())
5461
    {
5462
        MSG_ERROR("No system slider graphics found!");
5463
        return SkBitmap();
5464
    }
5465
 
5466
    SkPaint paint;
5467
    paint.setBlendMode(SkBlendMode::kSrc);
5468
 
5469
    if (!allocPixels(width, height, &slButton))
5470
        return slButton;
5471
 
5472
    slButton.eraseColor(SK_ColorTRANSPARENT);
5473
    SkCanvas slCan(slButton, SkSurfaceProps());
5474
    vector<SLIDER_t>::iterator sltIter;
5475
    // Loop through list of slider graphic files
5476
    for (sltIter = sltList.begin(); sltIter != sltList.end(); ++sltIter)
5477
    {
5478
        SkBitmap slPart;
5479
        SkBitmap slPartAlpha;
5480
        SkRect dst;
5481
 
5482
        if (dr.compare("horizontal") != 0 && (sltIter->type == SGR_LEFT || sltIter->type == SGR_RIGHT || sltIter->type == SGR_VERTICAL))    // vertical slider
5483
        {
5484
            if (!retrieveImage(sltIter->path, &slPart))     // Get the mask
5485
            {
5486
                MSG_ERROR("Missing slider button mask image " << sltIter->path);
5487
                return SkBitmap();
5488
            }
5489
 
5490
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))   // Get the alpha mask
5491
            {
5492
                MSG_ERROR("Missing slider button alpha image " << sltIter->pathAlpha);
5493
                return SkBitmap();
5494
            }
5495
 
5496
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
5497
 
5498
            if (sl.empty())
5499
                return sl;
5500
 
5501
            switch (sltIter->type)
5502
            {
5503
                case SGR_LEFT:      dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
5504
 
5505
                case SGR_VERTICAL:
5506
                    stretchImageWidth(&sl, sst.fixedSize);
5507
                    dst.setXYWH(sst.fixedSize / 2, 0, sl.info().width(), sl.info().height());
5508
                break;
5509
 
5510
                case SGR_RIGHT:     dst.setXYWH((sst.fixedSize / 2) + sst.fixedSize, 0, sl.info().width(), sl.info().height()); break;
5511
 
5512
                default:
5513
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
5514
            }
5515
 
5516
            sk_sp<SkImage> _image = SkImages::RasterFromBitmap(sl);
5517
            slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
5518
        }
5519
        else if (dr.compare("horizontal") == 0 && (sltIter->type == SGR_TOP || sltIter->type == SGR_BOTTOM || sltIter->type == SGR_HORIZONTAL)) // horizontal slider
5520
        {
5521
            if (!retrieveImage(sltIter->path, &slPart))
5522
            {
5523
                MSG_ERROR("Missing slider button image " << sltIter->path);
5524
                return SkBitmap();
5525
            }
5526
 
5527
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))
5528
            {
5529
                MSG_ERROR("Missing slider button image " << sltIter->pathAlpha);
5530
                return SkBitmap();
5531
            }
5532
 
5533
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
5534
 
5535
            if (sl.empty())
5536
                return sl;
5537
 
5538
            switch (sltIter->type)
5539
            {
5540
                case SGR_TOP:       dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
5541
 
5542
                case SGR_HORIZONTAL:
5543
                    stretchImageHeight(&sl, sst.fixedSize);
5544
                    dst.setXYWH(0, sst.fixedSize / 2, sl.info().width(), sl.info().height());
5545
                break;
5546
 
5547
                case SGR_BOTTOM:    dst.setXYWH(0, (sst.fixedSize / 2) + sst.fixedSize, sl.info().width(), sl.info().height()); break;
5548
 
5549
                default:
5550
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
5551
            }
5552
 
5553
            sk_sp<SkImage> _image = SkImages::RasterFromBitmap(sl);
5554
            slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
5555
        }
5556
    }
5557
 
5558
    return slButton;
5559
}
5560
 
5561
bool TButton::buttonIcon(SkBitmap* bm, int instance)
5562
{
5563
    DECL_TRACER("TButton::buttonIcon(SkBitmap* bm, int instance)");
5564
 
5565
    if (instance < 0 || (size_t)instance >= sr.size())
5566
    {
5567
        MSG_ERROR("Invalid instance " << instance);
5568
        return false;
5569
    }
5570
 
5571
    if (sr[instance].ii <= 0)
5572
    {
5573
        MSG_TRACE("No icon defined!");
5574
        return true;
5575
    }
5576
 
5577
    MSG_DEBUG("Drawing an icon ...");
5578
 
5579
    if (!gIcons)
5580
    {
5581
        MSG_WARNING("No icons were defined!");
5582
        return true;
5583
    }
5584
 
5585
    string file = gIcons->getFile(sr[instance].ii);
5586
 
5587
    if (file.empty())
5588
    {
5589
        MSG_WARNING("The icon " << sr[instance].ii << " was not found in table!");
5590
        return true;
5591
    }
5592
 
5593
    MSG_DEBUG("Loading icon file " << file);
5594
    sk_sp<SkData> image;
5595
    SkBitmap icon;
5596
 
5597
    if (!(image = readImage(file)))
5598
        return true;
5599
 
5600
    DecodeDataToBitmap(image, &icon);
5601
 
5602
    if (icon.empty())
5603
    {
5604
        MSG_WARNING("Could not create an icon for element " << sr[instance].ii << " on button " << bi << " (" << na << ")");
5605
        return true;
5606
    }
5607
 
5608
    SkImageInfo info = icon.info();
5609
    POSITION_t position = calcImagePosition(icon.width(), icon.height(), SC_ICON, instance);
5610
 
5611
    if (!position.valid)
5612
    {
5613
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
5614
        TError::setError();
5615
        return false;
5616
    }
5617
 
5618
    MSG_DEBUG("Putting Icon on top of bitmap ...");
5619
    SkPaint paint;
5620
    paint.setBlendMode(SkBlendMode::kSrcOver);
5621
    SkCanvas can(*bm, SkSurfaceProps());
5622
 
5623
    if (position.overflow)
5624
    {
5625
        SkRect irect;
5626
        SkRect bdst;
5627
        SkBitmap dst;
5628
        int left = (position.left >= 0) ? 0 : position.left * -1;
5629
        int top = (position.top >= 0) ? 0 : position.top * -1;
5630
        int width = std::min(wt, info.width());
5631
        int height = std::min(ht, info.height());
5632
        irect.setXYWH(left, top, width, height);
5633
        bm->getBounds(&bdst);
5634
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(icon);
5635
        can.drawImageRect(_image, irect, bdst, SkSamplingOptions(), &paint, SkCanvas::kStrict_SrcRectConstraint);
5636
    }
5637
    else
5638
    {
5639
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(icon);
5640
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
5641
    }
5642
 
5643
    return true;
5644
}
5645
 
5646
bool TButton::buttonText(SkBitmap* bm, int inst)
5647
{
5648
    DECL_TRACER("TButton::buttonText(SkBitmap* bm, int inst)");
5649
 
5650
    int instance = inst;
5651
 
5652
    if ((size_t)instance >= sr.size())
5653
        instance = (int)(sr.size() - 1);
5654
    else if (instance < 0)
5655
        instance = 0;
5656
 
5657
    if (sr[instance].te.empty())            // Is there a text?
5658
    {                                       // No, then return
5659
        MSG_DEBUG("Empty text string.");
5660
        return true;
5661
    }
5662
 
5663
    if (!mFonts)                            // Do we have any fonts?
5664
    {                                       // No, warn and return
5665
        MSG_WARNING("No fonts available to write a text!");
5666
        return true;
5667
    }
5668
 
462 andreas 5669
    sk_sp<SkTypeface> typeFace;
5670
    FONT_T font;
446 andreas 5671
 
462 andreas 5672
    if (gPageManager && !gPageManager->getSettings()->isTP5())
446 andreas 5673
    {
462 andreas 5674
        MSG_DEBUG("Searching for font number " << sr[instance].fi << " with text " << sr[instance].te);
5675
        font = mFonts->getFont(sr[instance].fi);
5676
 
5677
        if (font.file.empty())
5678
        {
5679
            MSG_WARNING("No font file name found for font " << sr[instance].fi);
5680
            return true;
5681
        }
5682
 
5683
        typeFace = mFonts->getTypeFace(sr[instance].fi);
446 andreas 5684
    }
462 andreas 5685
    else
5686
    {
5687
        MSG_DEBUG("Searching for font " << sr[instance].ff << " with size " << sr[instance].fs << " and text " << sr[instance].te);
5688
        font.file = sr[instance].ff;
5689
        font.size = sr[instance].fs;
5690
        typeFace = mFonts->getTypeFace(sr[instance].ff);
5691
        SkString family;
5692
        font.fullName = font.name = sr[instance].ff;
5693
    }
446 andreas 5694
 
5695
    SkCanvas canvas(*bm);
5696
 
5697
    if (!typeFace)
5698
    {
5699
        MSG_WARNING("Error creating type face " << font.fullName);
5700
    }
5701
 
5702
    SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
5703
    SkFont skFont;
5704
 
5705
    if (typeFace && typeFace->countTables() > 0)
5706
        skFont.setTypeface(typeFace);
5707
 
5708
    skFont.setSize(fontSizePt);
5709
    skFont.setEdging(SkFont::Edging::kAntiAlias);
5710
    MSG_DEBUG("Wanted font size: " << font.size << ", this is " << fontSizePt << " pt");
5711
 
5712
    SkPaint paint;
5713
    paint.setAntiAlias(true);
5714
    paint.setColor(TColor::getSkiaColor(sr[instance].ct));
5715
    paint.setStyle(SkPaint::kFill_Style);
5716
 
5717
    SkFontMetrics metrics;
5718
    skFont.getMetrics(&metrics);
5719
    int lines = numberLines(sr[instance].te);
5720
//    MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
5721
//    MSG_DEBUG("fCapHeight:    " << metrics.fCapHeight);
5722
//    MSG_DEBUG("fAscent:       " << metrics.fAscent);
5723
//    MSG_DEBUG("fDescent:      " << metrics.fDescent);
5724
//    MSG_DEBUG("fLeading:      " << metrics.fLeading);
5725
//    MSG_DEBUG("fXHeight:      " << metrics.fXHeight);
5726
 
5727
    MSG_DEBUG("Found " << lines << " lines.");
5728
 
5729
    if (lines > 1 || sr[instance].ww)
5730
    {
5731
        vector<string> textLines;
5732
 
5733
        if (!sr[instance].ww)
5734
        {
5735
            textLines = splitLine(sr[instance].te, true);
5736
            lines = static_cast<int>(textLines.size());
5737
        }
5738
        else
5739
        {
5740
            textLines = splitLine(sr[instance].te, wt, ht, skFont, paint);
5741
            lines = static_cast<int>(textLines.size());
5742
        }
5743
 
5744
        MSG_DEBUG("Calculated number of lines: " << lines);
5745
        int lineHeight = (metrics.fAscent * -1) + metrics.fDescent;
5746
        int totalHeight = lineHeight * lines;
5747
/*
5748
        if (totalHeight > ht)
5749
        {
5750
            lines = ht / lineHeight;
5751
            totalHeight = lineHeight * lines;
5752
        }
5753
*/
5754
        MSG_DEBUG("Line height: " << lineHeight << ", total height: " << totalHeight);
5755
        vector<string>::iterator iter;
5756
        int line = 0;
5757
        int maxWidth = 0;
5758
 
5759
        if (textLines.size() > 0)
5760
        {
5761
            // Calculate the maximum width
5762
            for (iter = textLines.begin(); iter != textLines.end(); ++iter)
5763
            {
5764
                SkRect rect;
5765
                skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
5766
 
5767
                if (rect.width() > maxWidth)
5768
                    maxWidth = rect.width();
5769
            }
5770
 
5771
            POSITION_t pos = calcImagePosition(maxWidth, totalHeight, SC_TEXT, instance);
5772
 
5773
            if (!pos.valid)
5774
            {
5775
                MSG_ERROR("Error calculating the text position!");
5776
                TError::setError();
5777
                return false;
5778
            }
5779
 
5780
            SkScalar lnHt = metrics.fAscent * -1;
5781
 
5782
            for (iter = textLines.begin(); iter != textLines.end(); ++iter)
5783
            {
5784
                sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
5785
                MSG_DEBUG("Trying to print line: " << *iter);
5786
                // We want to take care about the horizontal position.
5787
                SkRect rect;
5788
                skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
5789
                SkScalar horizontal = 0.0;
5790
 
5791
                switch(sr[instance].jt)
5792
                {
5793
                    case ORI_BOTTOM_MIDDLE:
5794
                    case ORI_CENTER_MIDDLE:
5795
                    case ORI_TOP_MIDDLE:
5796
                        horizontal = (wt - rect.width()) / 2.0f;
5797
                    break;
5798
 
5799
                    case ORI_BOTTOM_RIGHT:
5800
                    case ORI_CENTER_RIGHT:
5801
                    case ORI_TOP_RIGHT:
5802
                        horizontal = wt - rect.width();
5803
                    break;
5804
 
5805
                    default:
5806
                        horizontal = pos.left;
5807
                }
5808
 
5809
                SkScalar startX = horizontal;
5810
                SkScalar startY = (SkScalar)pos.top + (SkScalar)lineHeight * (SkScalar)line;
5811
                MSG_DEBUG("x=" << startX << ", y=" << startY);
5812
                bool tEffect = false;
5813
                // Text effects
5814
                if (sr[instance].et > 0)
5815
                    tEffect = textEffect(&canvas, blob, startX, startY + lnHt, instance);
5816
 
5817
                if (!tEffect)
5818
                    canvas.drawTextBlob(blob.get(), startX, startY + lnHt, paint);
5819
 
5820
                line++;
5821
 
5822
                if (line > lines)
5823
                    break;
5824
            }
5825
        }
5826
    }
5827
    else    // single line
5828
    {
5829
        string text = sr[instance].te;
5830
        sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.data(), skFont);
5831
        SkRect rect;
5832
        skFont.measureText(text.data(), text.size(), SkTextEncoding::kUTF8, &rect, &paint);
5833
        MSG_DEBUG("Calculated Skia rectangle of font: width=" << rect.width() << ", height=" << rect.height());
5834
        POSITION_t position;
5835
 
5836
        if (metrics.fCapHeight >= 1.0)
5837
            position = calcImagePosition(rect.width(), metrics.fCapHeight, SC_TEXT, instance);
5838
        else
5839
            position = calcImagePosition(rect.width(), rect.height(), SC_TEXT, instance);
5840
 
5841
        if (!position.valid)
5842
        {
5843
            MSG_ERROR("Error calculating the text position!");
5844
            TError::setError();
5845
            return false;
5846
        }
5847
 
5848
        MSG_DEBUG("Printing line " << text);
5849
        SkScalar startX = (SkScalar)position.left;
5850
        SkScalar startY = (SkScalar)position.top;
5851
 
5852
        if (metrics.fCapHeight >= 1.0)
5853
            startY += metrics.fCapHeight;   // This is the offset of the line
5854
        else
5855
            startY += rect.height();        // This is the offset of the line
5856
 
5857
        FONT_TYPE sym = TFont::isSymbol(typeFace);
5858
        bool tEffect = false;
5859
        // Text effects
5860
        if (sr[instance].et > 0)
5861
            tEffect = textEffect(&canvas, blob, startX, startY, instance);
5862
 
5863
        if (!tEffect && utf8Strlen(text) > 1)
5864
            canvas.drawTextBlob(blob.get(), startX, startY, paint);
5865
        else
5866
        {
5867
            int count = 0;
5868
            uint16_t *glyphs = nullptr;
5869
 
5870
            if (sym == FT_SYM_MS)
5871
            {
5872
                MSG_DEBUG("Microsoft proprietary symbol font detected.");
5873
                uint16_t *uni;
5874
                size_t num = TFont::utf8ToUtf16(text, &uni, true);
5875
                MSG_DEBUG("Got " << num << " unichars, first unichar: " << std::hex << std::setw(4) << std::setfill('0') << *uni << std::dec);
5876
 
5877
                if (num > 0)
5878
                {
5879
                    glyphs = new uint16_t[num];
5880
                    size_t glyphSize = sizeof(uint16_t) * num;
5881
                    count = skFont.textToGlyphs(uni, num, SkTextEncoding::kUTF16, glyphs, (int)glyphSize);
5882
 
5883
                    if (count <= 0)
5884
                    {
5885
                        delete[] glyphs;
5886
                        glyphs = TFont::textToGlyphs(text, typeFace, &num);
5887
                        count = (int)num;
5888
                    }
5889
                }
5890
                else
5891
                {
5892
                    canvas.drawTextBlob(blob.get(), startX, startY, paint);
5893
                    return true;
5894
                }
5895
 
5896
                if (uni)
5897
                    delete[] uni;
5898
            }
5899
            else if (tEffect)
5900
                return true;
5901
            else
5902
            {
5903
                glyphs = new uint16_t[text.size()];
5904
                size_t glyphSize = sizeof(uint16_t) * text.size();
5905
                count = skFont.textToGlyphs(text.data(), text.size(), SkTextEncoding::kUTF8, glyphs, (int)glyphSize);
5906
            }
5907
 
5908
            if (glyphs && count > 0)
5909
            {
5910
                MSG_DEBUG("1st glyph: 0x" << std::hex << std::setw(8) << std::setfill('0') << *glyphs << ", # glyphs: " << std::dec << count);
5911
                canvas.drawSimpleText(glyphs, sizeof(uint16_t) * count, SkTextEncoding::kGlyphID, startX, startY, skFont, paint);
5912
            }
5913
            else    // Try to print something
5914
            {
5915
                MSG_WARNING("Got no glyphs! Try to print: " << text);
5916
                canvas.drawString(text.data(), startX, startY, skFont, paint);
5917
            }
5918
 
5919
            if (glyphs)
5920
                delete[] glyphs;
5921
        }
5922
    }
5923
 
5924
    return true;
5925
}
5926
 
5927
int TButton::calcLineHeight(const string& text, SkFont& font)
5928
{
5929
    DECL_TRACER("TButton::calcLineHeight(const string& text, SkFont& font)");
5930
 
5931
    size_t pos = text.find("\n");       // Search for a line break.
5932
    string lText = text;
5933
 
5934
    if (pos != string::npos)            // Do we have found a line break?
5935
        lText = text.substr(0, pos - 1);// Yes, take only the text up to 1 before the line break (only 1 line).
5936
 
5937
    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(lText.c_str(), font);
5938
    SkRect rect = blob.get()->bounds();
5939
    return rect.height();
5940
}
5941
 
5942
bool TButton::textEffect(SkCanvas *canvas, sk_sp<SkTextBlob>& blob, SkScalar startX, SkScalar startY, int instance)
5943
{
5944
    DECL_TRACER("TButton::textEffect(SkBitmap *bm, int instance)");
5945
 
5946
    if (!canvas)
5947
        return false;
5948
 
5949
    if (instance < 0 || (size_t)instance >= sr.size())
5950
    {
5951
        MSG_ERROR("Invalid instance " << instance);
5952
        return false;
5953
    }
5954
 
5955
    // Drop Shadow
5956
    if (sr[instance].et >= 9 && sr[instance].et <= 32)
5957
    {
5958
        SkScalar gap = 0.0;
5959
        SkScalar sigma = 0.0;
5960
        SkScalar xDrop = 0.0;
5961
        SkScalar yDrop = 0.0;
5962
        uint8_t blurAlpha = 255;
5963
        SkPaint paint;
5964
        paint.setAntiAlias(true);
5965
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
5966
 
5967
        // Soft drop shadow
5968
        if (sr[instance].et >= 9 && sr[instance].et <= 16)
5969
        {
5970
            gap = (SkScalar)sr[instance].et - 8.0f;
5971
            sigma = 3.0f;
5972
            blurAlpha = 127;
5973
        }
5974
        else if (sr[instance].et >= 17 && sr[instance].et <= 24) // Medium drop shadow
5975
        {
5976
            gap = (SkScalar)sr[instance].et - 16.0f;
5977
            sigma = 2.0f;
5978
            blurAlpha = 159;
5979
        }
5980
        else    // Hard drop shadow
5981
        {
5982
            gap = (SkScalar)sr[instance].et - 24.0f;
5983
            sigma = 1.1f;
5984
            blurAlpha = 207;
5985
        }
5986
 
5987
        xDrop = gap;
5988
        yDrop = gap;
5989
        SkPaint blur(paint);
5990
        blur.setAlpha(blurAlpha);
5991
        blur.setColor(TColor::getSkiaColor(sr[instance].ec));
5992
        blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, 0));
5993
//        blur.setMaskFilter(SkImageFilters::Blur(sigma, sigma, 0));
5994
        canvas->drawTextBlob(blob.get(), startX + xDrop, startY + yDrop, blur);
5995
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
5996
        return true;
5997
    }
5998
    else if (sr[instance].et >= 5 && sr[instance].et <= 8)  // Glow
5999
    {
6000
        SkScalar sigma = 0.0;
6001
 
6002
        switch(sr[instance].et)
6003
        {
6004
            case 5: sigma = 2.0; break;     // Glow-S
6005
            case 6: sigma = 4.0; break;     // Glow-M
6006
            case 7: sigma = 6.0; break;     // Glow-L
6007
            case 8: sigma = 8.0; break;     // Glow-X
6008
        }
6009
 
6010
        SkPaint paint, blur;
6011
        paint.setAntiAlias(true);
6012
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
6013
        blur.setColor(TColor::getSkiaColor(sr[instance].ec));
6014
        blur.setStyle(SkPaint::kStroke_Style);
6015
        blur.setStrokeWidth(sigma / 1.5);
6016
        blur.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, sigma));
6017
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
6018
        canvas->drawTextBlob(blob.get(), startX, startY, blur);
6019
        return true;
6020
    }
6021
    else if (sr[instance].et >= 1 && sr[instance].et <= 4)  // Outline
6022
    {
6023
        SkScalar sigma = 0.0;
6024
 
6025
        switch(sr[instance].et)
6026
        {
6027
            case 1: sigma = 1.0; break;     // Outline-S
6028
            case 2: sigma = 2.0; break;     // Outline-M
6029
            case 3: sigma = 4.0; break;     // Outline-L
6030
            case 4: sigma = 6.0; break;     // Outline-X
6031
        }
6032
 
6033
        SkPaint paint, outline;
6034
        paint.setAntiAlias(true);
6035
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
6036
        outline.setAntiAlias(true);
6037
        outline.setColor(TColor::getSkiaColor(sr[instance].ec));
6038
        outline.setStyle(SkPaint::kStroke_Style);
6039
        outline.setStrokeWidth(sigma);
6040
        canvas->drawTextBlob(blob.get(), startX, startY, outline);
6041
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
6042
        return true;
6043
    }
6044
 
6045
    return false;
6046
}
6047
 
6048
/**
6049
 * @brief TButton::buttonBorder - draw a border, if any.
6050
 * This method draws a border if there is one defined in \b sr[].bs. If there
6051
 * is also a global border defined in \b bs then this border is limiting the
6052
 * valid borders to it. The method does not check this, because it is subject
6053
 * to TPDesign.
6054
 *
6055
 * @param bm        Bitmap to draw the border on.
6056
 * @param inst      The instance where the border definitition should be taken from
6057
 * @param lnType    This can be used to define one of the border types
6058
 *                      off, on, drag or drop
6059
 *
6060
 * @return TRUE on success, otherwise FALSE.
6061
 */
6062
bool TButton::buttonBorder(SkBitmap* bm, int inst, TSystemDraw::LINE_TYPE_t lnType)
6063
{
6064
    DECL_TRACER("TButton::buttonBorder(SkBitmap* bm, int instance, TSystemDraw::LINE_TYPE_t lnType)");
6065
 
6066
    TSystemDraw::LINE_TYPE_t lineType = lnType;
6067
    int instance = inst;
6068
 
6069
    if (instance < 0)
6070
        instance = 0;
6071
    else if (static_cast<size_t>(instance) > sr.size())
6072
        instance = static_cast<int>(sr.size()) - 1;
6073
 
6074
    if (sr[instance].bs.empty())
6075
    {
6076
        MSG_DEBUG("No border defined.");
6077
        return true;
6078
    }
6079
 
6080
    string bname = sr[instance].bs;
6081
    // Try to find the border in the system table
6082
    if (drawBorder(bm, bname, wt, ht, sr[instance].cb))
6083
        return true;
6084
 
6085
    // The border was not found or defined to be not drawn. Therefor we look
6086
    // into the system directory (__system/graphics/borders). If the wanted
6087
    // border exists there, we're drawing it.
6088
    BORDER_t bd;
6089
    int numBorders = 0;
6090
 
6091
    if (sr.size() == 2)
6092
    {
6093
        string n = bname;
6094
 
6095
        if ((StrContains(toLower(n), "inset") || StrContains(n, "active on")) && lineType == TSystemDraw::LT_OFF)
6096
            lineType = TSystemDraw::LT_ON;
6097
 
6098
        if (gPageManager->getSystemDraw()->getBorder(bname, lineType, &bd))
6099
            numBorders++;
6100
    }
6101
    else if (lineType == TSystemDraw::LT_OFF && gPageManager->getSystemDraw()->getBorder(bname, TSystemDraw::LT_ON, &bd))
6102
        numBorders++;
6103
    else if (gPageManager->getSystemDraw()->getBorder(bname, lineType, &bd))
6104
        numBorders++;
6105
 
6106
    if (numBorders > 0)
6107
    {
6108
        SkColor color = TColor::getSkiaColor(sr[instance].cb);      // border color
6109
        MSG_DEBUG("Button color: #" << std::setw(6) << std::setfill('0') << std::hex << color);
6110
        // Load images
6111
        SkBitmap imgB, imgBR, imgR, imgTR, imgT, imgTL, imgL, imgBL;
6112
 
6113
        if (!getBorderFragment(bd.b, bd.b_alpha, &imgB, color) || imgB.empty())
6114
            return false;
6115
 
6116
        MSG_DEBUG("Got images \"" << bd.b << "\" and \"" << bd.b_alpha << "\" with size " << imgB.info().width() << " x " << imgB.info().height());
6117
        if (!getBorderFragment(bd.br, bd.br_alpha, &imgBR, color) || imgBR.empty())
6118
            return false;
6119
 
6120
        MSG_DEBUG("Got images \"" << bd.br << "\" and \"" << bd.br_alpha << "\" with size " << imgBR.info().width() << " x " << imgBR.info().height());
6121
        if (!getBorderFragment(bd.r, bd.r_alpha, &imgR, color) || imgR.empty())
6122
            return false;
6123
 
6124
        MSG_DEBUG("Got images \"" << bd.r << "\" and \"" << bd.r_alpha << "\" with size " << imgR.info().width() << " x " << imgR.info().height());
6125
        if (!getBorderFragment(bd.tr, bd.tr_alpha, &imgTR, color) || imgTR.empty())
6126
            return false;
6127
 
6128
        MSG_DEBUG("Got images \"" << bd.tr << "\" and \"" << bd.tr_alpha << "\" with size " << imgTR.info().width() << " x " << imgTR.info().height());
6129
        if (!getBorderFragment(bd.t, bd.t_alpha, &imgT, color) || imgT.empty())
6130
            return false;
6131
 
6132
        MSG_DEBUG("Got images \"" << bd.t << "\" and \"" << bd.t_alpha << "\" with size " << imgT.info().width() << " x " << imgT.info().height());
6133
        if (!getBorderFragment(bd.tl, bd.tl_alpha, &imgTL, color) || imgTL.empty())
6134
            return false;
6135
 
6136
        MSG_DEBUG("Got images \"" << bd.tl << "\" and \"" << bd.tl_alpha << "\" with size " << imgTL.info().width() << " x " << imgTL.info().height());
6137
        if (!getBorderFragment(bd.l, bd.l_alpha, &imgL, color) || imgL.empty())
6138
            return false;
6139
 
6140
        mBorderWidth = imgL.info().width();
6141
        MSG_DEBUG("Got images \"" << bd.l << "\" and \"" << bd.l_alpha << "\" with size " << imgL.info().width() << " x " << imgL.info().height());
6142
 
6143
        if (!getBorderFragment(bd.bl, bd.bl_alpha, &imgBL, color) || imgBL.empty())
6144
            return false;
6145
 
6146
        MSG_DEBUG("Got images \"" << bd.bl << "\" and \"" << bd.bl_alpha << "\" with size " << imgBL.info().width() << " x " << imgBL.info().height());
6147
        MSG_DEBUG("Button image size: " << (imgTL.info().width() + imgT.info().width() + imgTR.info().width()) << " x " << (imgTL.info().height() + imgL.info().height() + imgBL.info().height()));
6148
        MSG_DEBUG("Total size: " << wt << " x " << ht);
6149
        stretchImageWidth(&imgB, wt - imgBL.info().width() - imgBR.info().width());
6150
        stretchImageWidth(&imgT, wt - imgTL.info().width() - imgTR.info().width());
6151
        stretchImageHeight(&imgL, ht - imgTL.info().height() - imgBL.info().height());
6152
        stretchImageHeight(&imgR, ht - imgTR.info().height() - imgBR.info().height());
6153
        MSG_DEBUG("Stretched button image size: " << (imgTL.info().width() + imgT.info().width() + imgTR.info().width()) << " x " << (imgTL.info().height() + imgL.info().height() + imgBL.info().height()));
6154
        // Draw the frame
6155
        SkBitmap frame;
6156
        allocPixels(bm->info().width(), bm->info().height(), &frame);
6157
        frame.eraseColor(SK_ColorTRANSPARENT);
6158
        SkCanvas target(*bm, SkSurfaceProps());
6159
        SkCanvas canvas(frame, SkSurfaceProps());
6160
        SkPaint paint;
6161
 
6162
        paint.setBlendMode(SkBlendMode::kSrcOver);
6163
        paint.setAntiAlias(true);
6164
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgB);   // bottom
6165
        canvas.drawImage(_image, imgBL.info().width(), ht - imgB.info().height(), SkSamplingOptions(), &paint);
6166
        _image = SkImages::RasterFromBitmap(imgT);                  // top
6167
        canvas.drawImage(_image, imgTL.info().width(), 0, SkSamplingOptions(), &paint);
6168
        _image = SkImages::RasterFromBitmap(imgBR);                 // bottom right
6169
        canvas.drawImage(_image, wt - imgBR.info().width(), ht - imgBR.info().height(), SkSamplingOptions(), &paint);
6170
        _image = SkImages::RasterFromBitmap(imgTR);                 // top right
6171
        canvas.drawImage(_image, wt - imgTR.info().width(), 0, SkSamplingOptions(), &paint);
6172
        _image = SkImages::RasterFromBitmap(imgTL);                 // top left
6173
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
6174
        _image = SkImages::RasterFromBitmap(imgBL);                 // bottom left
6175
        canvas.drawImage(_image, 0, ht - imgBL.info().height(), SkSamplingOptions(), &paint);
6176
        _image = SkImages::RasterFromBitmap(imgL);                  // left
6177
        canvas.drawImage(_image, 0, imgTL.info().height(), SkSamplingOptions(), &paint);
6178
        _image = SkImages::RasterFromBitmap(imgR);                  // right
6179
        canvas.drawImage(_image, wt - imgR.info().width(), imgTR.info().height(), SkSamplingOptions(), &paint);
6180
 
6181
        erasePart(bm, frame, Border::ERASE_OUTSIDE, imgL.info().width());
6182
        _image = SkImages::RasterFromBitmap(frame);
6183
        paint.setBlendMode(SkBlendMode::kSrcATop);
6184
        target.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
6185
    }
6186
    else    // We try to draw a frame by forcing it to draw even the not to draw marked frames.
6187
        drawBorder(bm, bname, wt, ht, sr[instance].cb, true);
6188
 
6189
    return true;
6190
}
6191
 
6192
int TButton::numberLines(const string& str)
6193
{
6194
    DECL_TRACER("TButton::numberLines(const string& str)");
6195
 
6196
    int lines = 1;
6197
 
6198
    if (str.empty())
6199
        return lines;
6200
 
6201
    string::const_iterator iter;
6202
 
6203
    for (iter = str.begin(); iter != str.end(); ++iter)
6204
    {
6205
        if (*iter == '\n' ||
6206
            (type == TEXT_INPUT && dt == "multiple" && *iter == '|') ||
6207
            (sr[mActInstance].ww != 0 && *iter == '|'))
6208
            lines++;
6209
    }
6210
 
6211
    return lines;
6212
}
6213
 
6214
SkRect TButton::calcRect(int width, int height, int pen)
6215
{
6216
    DECL_TRACER("TButton::calcRect(int width, int height, int pen)");
6217
    SkRect rect;
6218
 
6219
    SkScalar left = (SkScalar)pen / 2.0;
6220
    SkScalar top = (SkScalar)pen / 2.0;
6221
    SkScalar w = (SkScalar)width - (SkScalar)pen;
6222
    SkScalar h = (SkScalar)height - (SkScalar)pen;
6223
    rect.setXYWH(left, top, w, h);
6224
    return rect;
6225
}
6226
 
6227
void TButton::runAnimation()
6228
{
6229
    DECL_TRACER("TButton::runAnimation()");
6230
 
6231
    if (mAniRunning)
6232
        return;
6233
 
6234
    mAniRunning = true;
6235
    int instance = 0;
6236
    int max = (int)sr.size();
6237
    ulong tm = nu * ru + nd * rd;
6238
 
6239
    while (mAniRunning && !mAniStop && !prg_stopped)
6240
    {
6241
        mActInstance = instance;
6242
        mChanged = true;
6243
 
6244
        if (visible && !drawButton(instance))
6245
            break;
6246
 
6247
        instance++;
6248
 
6249
        if (instance >= max)
6250
            instance = 0;
6251
 
6252
        std::this_thread::sleep_for(std::chrono::milliseconds(tm));
6253
    }
6254
 
6255
    mAniRunning = false;
6256
}
6257
 
6258
void TButton::runAnimationRange(int start, int end, ulong step)
6259
{
6260
    DECL_TRACER("TButton::runAnimationRange(int start, int end, ulong step)");
6261
 
6262
    if (mAniRunning)
6263
        return;
6264
 
6265
    mAniRunning = true;
6266
    int instance = start - 1;
6267
    int max = std::min(end, (int)sr.size());
6268
    std::chrono::steady_clock::time_point startt = std::chrono::steady_clock::now();
6269
 
6270
    while (mAniRunning && !mAniStop && !prg_stopped)
6271
    {
6272
        mActInstance = instance;
6273
        mChanged = true;
6274
 
6275
        if (visible)
6276
            drawButton(instance);   // We ignore the state and try to draw the next instance
6277
 
6278
        instance++;
6279
 
6280
        if (instance >= max)
6281
            instance = start - 1;
6282
 
6283
        std::this_thread::sleep_for(std::chrono::milliseconds(step));
6284
 
6285
        if (mAniRunTime > 0)
6286
        {
6287
            std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
6288
            std::chrono::nanoseconds difftime = current - startt;
6289
            ulong duration = std::chrono::duration_cast<std::chrono::milliseconds>(difftime).count();
6290
 
6291
            if (duration >= mAniRunTime)
6292
                break;
6293
        }
6294
    }
6295
 
6296
    mAniRunTime = 0;
6297
    mAniRunning = false;
6298
}
6299
 
6300
bool TButton::drawButtonMultistateAni()
6301
{
6302
    DECL_TRACER("TButton::drawButtonMultistateAni()");
6303
 
6304
    if (prg_stopped)
6305
        return true;
6306
 
6307
    if (!visible || hd)    // Do nothing if this button is invisible
6308
        return true;
6309
 
6310
    if (mAniRunning || mThrAni.joinable())
6311
    {
6312
        MSG_TRACE("Animation is already running!");
6313
        return true;
6314
    }
6315
 
6316
    try
6317
    {
6318
        mAniStop = false;
6319
        mThrAni = thread([=] { runAnimation(); });
6320
        mThrAni.detach();
6321
    }
6322
    catch (exception& e)
6323
    {
6324
        MSG_ERROR("Error starting the button animation thread: " << e.what());
6325
        return false;
6326
    }
6327
 
6328
    return true;
6329
}
6330
 
6331
bool TButton::drawButton(int instance, bool show, bool subview)
6332
{
6333
    DECL_TRACER("TButton::drawButton(int instance, bool show, bool subview)");
6334
 
6335
    if (prg_stopped)
6336
        return false;
6337
 
6338
    if (subview)
6339
        mSubViewPart = subview;
6340
 
6341
    if ((size_t)instance >= sr.size() || instance < 0)
6342
    {
6343
        MSG_ERROR("Instance " << instance << " is out of bounds!");
6344
        TError::setError();
6345
#if TESTMODE == 1
6346
        setScreenDone();
6347
#endif
6348
        return false;
6349
    }
6350
 
6351
    if (!_displayButton && gPageManager)
6352
        _displayButton = gPageManager->getCallbackDB();
6353
 
6354
    if (!visible || hd || instance != mActInstance || !_displayButton)
6355
    {
6356
        bool db = (_displayButton != nullptr);
6357
        MSG_DEBUG("Button " << bi << ", \"" << na << "\" at instance " << instance << " is not to draw!");
6358
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Hidden: " << (hd ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
6359
#if TESTMODE == 1
6360
        setScreenDone();
6361
#endif
6362
        return true;
6363
    }
6364
 
6365
    TError::clear();
6366
    MSG_DEBUG("Drawing button " << bi << ", \"" << na << "\" at instance " << instance);
6367
 
6368
    if (!mChanged && !mLastImage.empty())
6369
    {
6370
        if (show)
6371
        {
6372
            showLastButton();
6373
 
6374
            if (type == SUBPAGE_VIEW)
6375
            {
6376
                if (gPageManager)
6377
                    gPageManager->showSubViewList(st, this);
6378
            }
6379
        }
6380
 
6381
        return true;
6382
    }
6383
 
6384
    ulong parent = mHandle & 0xffff0000;
6385
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
6386
 
6387
    if (TError::isError())
6388
    {
6389
#if TESTMODE == 1
6390
        setScreenDone();
6391
#endif
6392
        return false;
6393
    }
6394
 
6395
    SkBitmap imgButton;
6396
 
6397
    if (!allocPixels(wt, ht, &imgButton))
6398
    {
6399
#if TESTMODE == 1
6400
        setScreenDone();
6401
#endif
6402
        return false;
6403
    }
6404
 
6405
    // We create an empty (transparent) image here. Later it depends on the
6406
    // draw order of the elements. If, for example, the background fill is
6407
    // not the first thing, we must be sure to not destroy already drawn
6408
    // elemts of the button.
6409
    imgButton.eraseColor(SkColors::kTransparent);
6410
    bool dynState = false;
6411
 
6412
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
6413
    {
6414
        if (mDOrder[i] == ORD_ELEM_FILL)
6415
        {
6416
            if (!buttonFill(&imgButton, instance))
6417
            {
6418
#if TESTMODE == 1
6419
                setScreenDone();
6420
#endif
6421
                return false;
6422
            }
6423
        }
6424
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
6425
        {
6426
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
6427
            {
6428
#if TESTMODE == 1
6429
                setScreenDone();
6430
#endif
6431
                return false;
6432
            }
6433
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, show, &dynState))
6434
            {
6435
#if TESTMODE == 1
6436
                setScreenDone();
6437
#endif
6438
                return false;
6439
            }
6440
        }
479 andreas 6441
        else if (!TTPInit::getTP5() && mDOrder[i] == ORD_ELEM_ICON)
446 andreas 6442
        {
6443
            if (!buttonIcon(&imgButton, instance))
6444
            {
6445
#if TESTMODE == 1
6446
                setScreenDone();
6447
#endif
6448
                return false;
6449
            }
6450
        }
6451
        else if (mDOrder[i] == ORD_ELEM_TEXT)
6452
        {
6453
            // If this is a marquee line, don't draw the text. This will be done
6454
            // by the surface.
6455
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
6456
                continue;
6457
 
6458
            if (!buttonText(&imgButton, instance))
6459
            {
6460
#if TESTMODE == 1
6461
                setScreenDone();
6462
#endif
6463
                return false;
6464
            }
6465
        }
6466
        else if (mDOrder[i] == ORD_ELEM_BORDER)
6467
        {
6468
            if (!buttonBorder(&imgButton, instance))
6469
            {
6470
#if TESTMODE == 1
6471
                setScreenDone();
6472
#endif
6473
                return false;
6474
            }
6475
        }
6476
    }
6477
 
6478
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
6479
    {
6480
        SkBitmap ooButton;
6481
        int w = imgButton.width();
6482
        int h = imgButton.height();
6483
 
6484
        if (!allocPixels(w, h, &ooButton))
6485
        {
6486
#if TESTMODE == 1
6487
            setScreenDone();
6488
#endif
6489
            return false;
6490
        }
6491
 
6492
        SkCanvas canvas(ooButton);
6493
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
6494
        SkRegion region;
6495
        region.setRect(irect);
6496
        SkScalar oo;
6497
 
6498
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
6499
        {
6500
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
6501
            MSG_DEBUG("Set global overal opacity to " << oo);
6502
        }
6503
        else if (sr[instance].oo >= 0)
6504
        {
6505
            oo = (SkScalar)sr[instance].oo;
6506
            MSG_DEBUG("Set overal opacity to " << oo);
6507
        }
6508
        else
6509
        {
6510
            oo = (SkScalar)mGlobalOO;
6511
            MSG_DEBUG("Set global overal opacity to " << oo);
6512
        }
6513
 
6514
        SkScalar alpha = 1.0 / 255.0 * oo;
6515
        MSG_DEBUG("Calculated alpha value: " << alpha);
6516
        SkPaint paint;
6517
        paint.setAlphaf(alpha);
6518
//        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
6519
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
6520
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
6521
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
6522
        imgButton = ooButton;
6523
    }
6524
 
6525
    mLastImage = imgButton;
6526
    mChanged = false;
6527
 
6528
    if (!prg_stopped && !dynState)
6529
    {
6530
        int rwidth = wt;
6531
        int rheight = ht;
6532
        int rleft = mPosLeft;
6533
        int rtop = mPosTop;
6534
#ifdef _SCALE_SKIA_
6535
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6536
        {
6537
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6538
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6539
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
6540
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
6541
 
6542
            SkPaint paint;
6543
            paint.setBlendMode(SkBlendMode::kSrc);
6544
            paint.setFilterQuality(kHigh_SkFilterQuality);
6545
            // Calculate new dimension
6546
            SkImageInfo info = imgButton.info();
6547
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
6548
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
6549
            // Create a canvas and draw new image
6550
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
6551
            imgButton.allocN32Pixels(width, height);
6552
            imgButton.eraseColor(SK_ColorTRANSPARENT);
6553
            SkCanvas can(imgButton, SkSurfaceProps());
6554
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
6555
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
6556
            rowBytes = imgButton.info().minRowBytes();
6557
            mLastImage = imgButton;
6558
        }
6559
#endif
6560
        if (show)
6561
        {
6562
            MSG_DEBUG("Button type: " << buttonTypeToString());
6563
 
6564
            if (type != SUBPAGE_VIEW && !mSubViewPart)
6565
            {
6566
                TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
6567
                _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
6568
 
6569
                if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
6570
                {
6571
                    if (gPageManager && gPageManager->getSetMarqueeText())
6572
                        gPageManager->getSetMarqueeText()(this);
6573
                }
6574
            }
6575
            else if (type != SUBPAGE_VIEW && mSubViewPart)
6576
            {
6577
                if (gPageManager)
6578
                    gPageManager->updateSubViewItem(this);
6579
            }
6580
        }
6581
    }
6582
 
6583
    if (!prg_stopped && type == SUBPAGE_VIEW && show)
6584
    {
6585
        if (gPageManager)
6586
            gPageManager->showSubViewList(st, this);
6587
    }
6588
 
6589
    return true;
6590
}
6591
 
6592
bool TButton::drawTextArea(int instance)
6593
{
6594
    DECL_TRACER("TButton::drawTextArea(int instance)");
6595
 
6596
    if (prg_stopped)
6597
    {
6598
#if TESTMODE == 1
6599
        setScreenDone();
6600
#endif
6601
        return false;
6602
    }
6603
 
6604
    if (!visible || hd)
6605
    {
6606
#if TESTMODE == 1
6607
        setScreenDone();
6608
#endif
6609
        return true;
6610
    }
6611
 
6612
    if ((size_t)instance >= sr.size() || instance < 0)
6613
    {
6614
        MSG_ERROR("Instance " << instance << " is out of bounds!");
6615
        TError::setError();
6616
#if TESTMODE == 1
6617
        setScreenDone();
6618
#endif
6619
        return false;
6620
    }
6621
 
6622
    if (!mChanged && !mLastImage.empty())
6623
    {
6624
        showLastButton();
6625
        return true;
6626
    }
6627
 
6628
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
6629
 
6630
    if (TError::isError())
6631
    {
6632
#if TESTMODE == 1
6633
        setScreenDone();
6634
#endif
6635
        return false;
6636
    }
6637
 
6638
    SkBitmap imgButton;
6639
 
6640
    if (!allocPixels(wt, ht, &imgButton))
6641
    {
6642
#if TESTMODE == 1
6643
        setScreenDone();
6644
#endif
6645
        return false;
6646
    }
6647
 
6648
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
6649
    {
6650
        if (mDOrder[i] == ORD_ELEM_FILL)
6651
        {
6652
            if (!buttonFill(&imgButton, instance))
6653
            {
6654
#if TESTMODE == 1
6655
                setScreenDone();
6656
#endif
6657
                return false;
6658
            }
6659
        }
6660
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
6661
        {
6662
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
6663
            {
6664
#if TESTMODE == 1
6665
                setScreenDone();
6666
#endif
6667
                return false;
6668
            }
6669
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, false))
6670
            {
6671
#if TESTMODE == 1
6672
                setScreenDone();
6673
#endif
6674
                return false;
6675
            }
6676
        }
6677
        else if (mDOrder[i] == ORD_ELEM_ICON)
6678
        {
6679
            if (!buttonIcon(&imgButton, instance))
6680
            {
6681
#if TESTMODE == 1
6682
                setScreenDone();
6683
#endif
6684
                return false;
6685
            }
6686
        }
6687
        else if (mDOrder[i] == ORD_ELEM_BORDER)
6688
        {
6689
            if (!buttonBorder(&imgButton, instance))
6690
            {
6691
#if TESTMODE == 1
6692
                setScreenDone();
6693
#endif
6694
                return false;
6695
            }
6696
        }
6697
    }
6698
 
6699
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
6700
    {
6701
        SkBitmap ooButton;
6702
        int w = imgButton.width();
6703
        int h = imgButton.height();
6704
 
6705
        if (!allocPixels(w, h, &ooButton))
6706
        {
6707
#if TESTMODE == 1
6708
            setScreenDone();
6709
#endif
6710
            return false;
6711
        }
6712
 
6713
        SkCanvas canvas(ooButton);
6714
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
6715
        SkRegion region;
6716
        region.setRect(irect);
6717
        SkScalar oo;
6718
 
6719
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
6720
        {
6721
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
6722
            MSG_DEBUG("Set global overal opacity to " << oo);
6723
        }
6724
        else if (sr[instance].oo >= 0)
6725
        {
6726
            oo = (SkScalar)sr[instance].oo;
6727
            MSG_DEBUG("Set overal opacity to " << oo);
6728
        }
6729
        else
6730
        {
6731
            oo = (SkScalar)mGlobalOO;
6732
            MSG_DEBUG("Set global overal opacity to " << oo);
6733
        }
6734
 
6735
        SkScalar alpha = 1.0 / 255.0 * oo;
6736
        MSG_DEBUG("Calculated alpha value: " << alpha);
6737
        SkPaint paint;
6738
        paint.setAlphaf(alpha);
6739
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
6740
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
6741
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
6742
        imgButton = ooButton;
6743
    }
6744
 
6745
    mLastImage = imgButton;
6746
    mChanged = false;
6747
 
6748
    if (!prg_stopped)
6749
    {
6750
        int rwidth = wt;
6751
        int rheight = ht;
6752
        int rleft = mPosLeft;
6753
        int rtop = mPosTop;
6754
        size_t rowBytes = imgButton.info().minRowBytes();
6755
#ifdef _SCALE_SKIA_
6756
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6757
        {
6758
            size_t rowBytes = imgButton.info().minRowBytes();
6759
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6760
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6761
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
6762
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
6763
 
6764
            SkPaint paint;
6765
            paint.setBlendMode(SkBlendMode::kSrc);
6766
            paint.setFilterQuality(kHigh_SkFilterQuality);
6767
            // Calculate new dimension
6768
            SkImageInfo info = imgButton.info();
6769
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
6770
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
6771
            // Create a canvas and draw new image
6772
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
6773
            imgButton.allocN32Pixels(width, height);
6774
            imgButton.eraseColor(SK_ColorTRANSPARENT);
6775
            SkCanvas can(imgButton, SkSurfaceProps());
6776
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
6777
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
6778
            rowBytes = imgButton.info().minRowBytes();
6779
            mLastImage = imgButton;
6780
        }
6781
#endif
6782
        if (gPageManager && gPageManager->getCallbackInputText())
6783
        {
6784
            BITMAP_t bm;
6785
            bm.buffer = (unsigned char *)imgButton.getPixels();
6786
            bm.rowBytes = rowBytes;
6787
            bm.left = rleft;
6788
            bm.top = rtop;
6789
            bm.width = rwidth;
6790
            bm.height = rheight;
6791
            gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
6792
        }
6793
    }
6794
 
6795
    return true;
6796
}
6797
 
6798
bool TButton::drawMultistateBargraph(int level, bool show)
6799
{
6800
    DECL_TRACER("TButton::drawMultistateBargraph(int level, bool show)");
6801
 
6802
    if (prg_stopped)
6803
    {
6804
#if TESTMODE == 1
6805
        setScreenDone();
6806
#endif
6807
        return false;
6808
    }
6809
 
6810
    if (!_displayButton && gPageManager)
6811
        _displayButton = gPageManager->getCallbackDB();
6812
 
6813
    if (!visible || hd || !_displayButton)
6814
    {
6815
        bool db = (_displayButton != nullptr);
6816
        MSG_DEBUG("Multistate bargraph " << bi << ", \"" << na << " is not to draw!");
6817
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
6818
#if TESTMODE == 1
6819
        setScreenDone();
6820
#endif
6821
        return true;
6822
    }
6823
 
6824
    int maxLevel = level;
6825
 
6826
    if (maxLevel > rh)
6827
        maxLevel = rh;
6828
    else if (maxLevel < rl)
6829
        maxLevel = rl;
6830
    else if (maxLevel < 0)
6831
        maxLevel = rl;
6832
 
6833
    MSG_DEBUG("Display instance " << maxLevel);
6834
    ulong parent = mHandle & 0xffff0000;
6835
    getDrawOrder(sr[maxLevel]._do, (DRAW_ORDER *)&mDOrder);
6836
 
6837
    if (TError::isError())
6838
    {
6839
#if TESTMODE == 1
6840
        setScreenDone();
6841
#endif
6842
        return false;
6843
    }
6844
 
6845
    SkBitmap imgButton;
6846
 
6847
    if (!allocPixels(wt, ht, &imgButton))
6848
    {
6849
#if TESTMODE == 1
6850
        setScreenDone();
6851
#endif
6852
        return false;
6853
    }
6854
 
6855
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
6856
    {
6857
        if (mDOrder[i] == ORD_ELEM_FILL)
6858
        {
6859
            if (!buttonFill(&imgButton, maxLevel))
6860
            {
6861
#if TESTMODE == 1
6862
                setScreenDone();
6863
#endif
6864
                return false;
6865
            }
6866
        }
6867
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
6868
        {
6869
            if (!buttonBitmap(&imgButton, maxLevel))
6870
            {
6871
#if TESTMODE == 1
6872
                setScreenDone();
6873
#endif
6874
                return false;
6875
            }
6876
        }
6877
        else if (mDOrder[i] == ORD_ELEM_ICON)
6878
        {
6879
            if (!buttonIcon(&imgButton, maxLevel))
6880
            {
6881
#if TESTMODE == 1
6882
                setScreenDone();
6883
#endif
6884
                return false;
6885
            }
6886
        }
6887
        else if (mDOrder[i] == ORD_ELEM_TEXT)
6888
        {
6889
            // If this is a marquee line, don't draw the text. This will be done
6890
            // by the surface.
6891
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
6892
                continue;
6893
 
6894
            if (!buttonText(&imgButton, maxLevel))
6895
            {
6896
#if TESTMODE == 1
6897
                setScreenDone();
6898
#endif
6899
                return false;
6900
            }
6901
        }
6902
        else if (mDOrder[i] == ORD_ELEM_BORDER)
6903
        {
6904
            if (!buttonBorder(&imgButton, maxLevel))
6905
            {
6906
#if TESTMODE == 1
6907
                setScreenDone();
6908
#endif
6909
                return false;
6910
            }
6911
        }
6912
    }
6913
 
6914
    if (mGlobalOO >= 0 || sr[maxLevel].oo >= 0) // Take overall opacity into consideration
6915
    {
6916
        SkBitmap ooButton;
6917
        int w = imgButton.width();
6918
        int h = imgButton.height();
6919
 
6920
        if (!allocPixels(w, h, &ooButton))
6921
        {
6922
#if TESTMODE == 1
6923
            setScreenDone();
6924
#endif
6925
            return false;
6926
        }
6927
 
6928
        SkCanvas canvas(ooButton);
6929
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
6930
        SkRegion region;
6931
        region.setRect(irect);
6932
        SkScalar oo;
6933
 
6934
        if (mGlobalOO >= 0 && sr[maxLevel].oo >= 0)
6935
        {
6936
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[maxLevel].oo);
6937
            MSG_DEBUG("Set global overal opacity to " << oo);
6938
        }
6939
        else if (sr[maxLevel].oo >= 0)
6940
        {
6941
            oo = (SkScalar)sr[maxLevel].oo;
6942
            MSG_DEBUG("Set overal opacity to " << oo);
6943
        }
6944
        else
6945
        {
6946
            oo = (SkScalar)mGlobalOO;
6947
            MSG_DEBUG("Set global overal opacity to " << oo);
6948
        }
6949
 
6950
        SkScalar alpha = 1.0 / 255.0 * oo;
6951
        MSG_DEBUG("Calculated alpha value: " << alpha);
6952
        SkPaint paint;
6953
        paint.setAlphaf(alpha);
6954
//        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
6955
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
6956
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
6957
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
6958
        imgButton = ooButton;
6959
    }
6960
 
6961
    mLastImage = imgButton;
6962
    mChanged = false;
6963
 
6964
    if (!prg_stopped)
6965
    {
6966
        int rwidth = wt;
6967
        int rheight = ht;
6968
        int rleft = mPosLeft;
6969
        int rtop = mPosTop;
6970
#ifdef _SCALE_SKIA_
6971
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6972
        {
6973
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6974
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6975
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
6976
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
6977
 
6978
            SkPaint paint;
6979
            paint.setBlendMode(SkBlendMode::kSrc);
6980
            paint.setFilterQuality(kHigh_SkFilterQuality);
6981
            // Calculate new dimension
6982
            SkImageInfo info = imgButton.info();
6983
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
6984
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
6985
            MSG_DEBUG("Button dimension: " << width << " x " << height);
6986
            // Create a canvas and draw new image
6987
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
6988
            imgButton.allocN32Pixels(width, height);
6989
            imgButton.eraseColor(SK_ColorTRANSPARENT);
6990
            SkCanvas can(imgButton, SkSurfaceProps());
6991
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
6992
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
6993
            MSG_DEBUG("Old rowBytes: " << rowBytes);
6994
            rowBytes = imgButton.info().minRowBytes();
6995
            MSG_DEBUG("New rowBytes: " << rowBytes);
6996
            mLastImage = imgButton;
6997
        }
6998
#endif
6999
        if (show)
7000
        {
7001
            TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
7002
            _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
7003
 
7004
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
7005
            {
7006
                if (gPageManager && gPageManager->getSetMarqueeText())
7007
                    gPageManager->getSetMarqueeText()(this);
7008
            }
7009
        }
7010
#if TESTMODE == 1
7011
        else
7012
            setScreenDone();
7013
#endif
7014
    }
7015
 
7016
    return true;
7017
}
7018
 
7019
void TButton::setBargraphInvert(int invert)
7020
{
7021
    DECL_TRACER("TButton::setBargraphInvert(int invert)");
7022
 
7023
    if (invert < 0 || invert > 3)
7024
        return;
7025
 
7026
    if (invert != ri)
7027
    {
7028
        ri = invert;
7029
        mChanged = true;
7030
    }
7031
 
7032
    int lastLevel = 0;
7033
    int lastJoyX = 0;
7034
    int lastJoyY = 0;
7035
 
7036
    if (gPageManager)
7037
    {
7038
        TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
7039
 
7040
        if (buttonStates)
7041
        {
7042
            lastLevel = buttonStates->getLastLevel();
7043
            lastJoyX = buttonStates->getLastJoyX();
7044
            lastJoyY = buttonStates->getLastJoyY();
7045
        }
7046
        else
7047
        {
7048
            MSG_ERROR("Button states not found!");
7049
            return;
7050
        }
7051
    }
7052
 
7053
    if (mChanged && lp && lv)
7054
    {
7055
        amx::ANET_SEND scmd;
7056
        scmd.device = TConfig::getChannel();
7057
        scmd.port = lp;
7058
        scmd.channel = lv;
7059
        scmd.level = lv;
7060
 
7061
        if (type == BARGRAPH)
7062
            scmd.value = (ri > 0 ? ((rh - rl) - lastLevel) : lastLevel);
7063
        else if (invert == 1 || invert == 3)
7064
            scmd.value = (ri > 0 ? ((rh - rl) - lastJoyX) : lastJoyX);
7065
 
7066
        scmd.MC = 0x008a;
7067
 
7068
        if (gAmxNet)
7069
            gAmxNet->sendCommand(scmd);
7070
 
7071
        if (type == JOYSTICK && (invert == 2 || invert == 3))
7072
        {
7073
            scmd.channel = lv;
7074
            scmd.level = lv;
7075
            scmd.value = (ri > 0 ? ((rh - rl) - lastJoyY) : lastJoyY);
7076
 
7077
            if (gAmxNet)
7078
                gAmxNet->sendCommand(scmd);
7079
        }
7080
    }
7081
}
7082
 
7083
void TButton::setBargraphRampDownTime(int t)
7084
{
7085
    DECL_TRACER("TButton::setBargraphRampDownTime(int t)");
7086
 
7087
    if (t < 0)
7088
        return;
7089
 
7090
    rd = t;
7091
}
7092
 
7093
void TButton::setBargraphRampUpTime(int t)
7094
{
7095
    DECL_TRACER("Button::TButton::setBargraphRampUpTime(int t)");
7096
 
7097
    if (t < 0)
7098
        return;
7099
 
7100
    ru = t;
7101
}
7102
 
7103
void TButton::setBargraphDragIncrement(int inc)
7104
{
7105
    DECL_TRACER("TButton::setBargraphDragIncrement(int inc)");
7106
 
7107
    if (inc < 0 || inc > (rh - rl))
7108
        return;
7109
 
7110
    rn = inc;
7111
}
7112
 
7113
/*
7114
 * The parameters "x" and "y" are the levels of the x and y axes.
7115
 */
7116
bool TButton::drawJoystick(int x, int y)
7117
{
7118
    DECL_TRACER("TButton::drawJoystick(int x, int y)");
7119
 
7120
    if (type != JOYSTICK)
7121
    {
7122
        MSG_ERROR("Element is no joystick!");
7123
        TError::setError();
7124
        return false;
7125
    }
7126
 
7127
    if (sr.empty())
7128
    {
7129
        MSG_ERROR("Joystick has no element!");
7130
        TError::setError();
7131
        return false;
7132
    }
7133
 
7134
    TButtonStates *buttonStates = getButtonState();
7135
 
7136
    if (!buttonStates)
7137
    {
7138
        MSG_ERROR("Button states not found!");
7139
        TError::setError();
7140
        return false;
7141
    }
7142
 
7143
    int lastJoyX = buttonStates->getLastJoyX();
7144
    int lastJoyY = buttonStates->getLastJoyY();
7145
 
7146
    if (!_displayButton && gPageManager)
7147
        _displayButton = gPageManager->getCallbackDB();
7148
 
7149
    if (!mChanged && lastJoyX == x && lastJoyY == y)
7150
    {
7151
        showLastButton();
7152
        return true;
7153
    }
7154
 
7155
    if (x < rl)
7156
        lastJoyX = rl;
7157
    else if (x > rh)
7158
        lastJoyX = rh;
7159
    else
7160
        lastJoyX = x;
7161
 
7162
    if (y < rl)
7163
        lastJoyY = rl;
7164
    else if (y > rh)
7165
        lastJoyY = rh;
7166
    else
7167
        lastJoyY = y;
7168
 
7169
    buttonStates->setLastJoyX(lastJoyX);
7170
    buttonStates->setLastJoyY(lastJoyY);
7171
 
7172
    if (!visible || hd || !_displayButton)
7173
    {
7174
        bool db = (_displayButton != nullptr);
7175
        MSG_DEBUG("Joystick " << bi << ", \"" << na << "\" with coordinates " << lastJoyX << "|" << lastJoyY << " is not to draw!");
7176
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
7177
        return true;
7178
    }
7179
 
7180
    ulong parent = mHandle & 0xffff0000;
7181
 
7182
    getDrawOrder(sr[0]._do, (DRAW_ORDER *)&mDOrder);
7183
 
7184
    if (TError::isError())
7185
        return false;
7186
 
7187
    SkBitmap imgButton;
7188
 
7189
    if (!allocPixels(wt, ht, &imgButton))
7190
        return false;
7191
 
7192
    imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
7193
    bool haveFrame = false;
7194
 
7195
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
7196
    {
7197
        if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
7198
        {
7199
            if (!buttonFill(&imgButton, 0))
7200
                return false;
7201
        }
7202
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
7203
        {
7204
            if (!drawJoystickCursor(&imgButton, lastJoyX, lastJoyY))
7205
                return false;
7206
        }
7207
        else if (mDOrder[i] == ORD_ELEM_ICON)
7208
        {
7209
            if (!buttonIcon(&imgButton, 0))
7210
                return false;
7211
        }
7212
        else if (mDOrder[i] == ORD_ELEM_TEXT)
7213
        {
7214
            if (!buttonText(&imgButton, 0))
7215
                return false;
7216
        }
7217
        else if (mDOrder[i] == ORD_ELEM_BORDER)
7218
        {
7219
            if (!buttonBorder(&imgButton, 0))
7220
                return false;
7221
 
7222
            haveFrame = true;
7223
        }
7224
    }
7225
 
7226
    if (mGlobalOO >= 0 || sr[0].oo >= 0) // Take overall opacity into consideration
7227
    {
7228
        SkBitmap ooButton;
7229
        int w = imgButton.width();
7230
        int h = imgButton.height();
7231
 
7232
        if (!allocPixels(w, h, &ooButton))
7233
            return false;
7234
 
7235
        SkCanvas canvas(ooButton);
7236
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
7237
        SkRegion region;
7238
        region.setRect(irect);
7239
        SkScalar oo;
7240
 
7241
        if (mGlobalOO >= 0 && sr[0].oo >= 0)
7242
        {
7243
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[0].oo);
7244
            MSG_DEBUG("Set global overal opacity to " << oo);
7245
        }
7246
        else if (sr[0].oo >= 0)
7247
        {
7248
            oo = (SkScalar)sr[0].oo;
7249
            MSG_DEBUG("Set overal opacity to " << oo);
7250
        }
7251
        else
7252
        {
7253
            oo = (SkScalar)mGlobalOO;
7254
            MSG_DEBUG("Set global overal opacity to " << oo);
7255
        }
7256
 
7257
        SkScalar alpha = 1.0 / 255.0 * oo;
7258
        MSG_DEBUG("Calculated alpha value: " << alpha);
7259
        SkPaint paint;
7260
        paint.setAlphaf(alpha);
7261
        //        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
7262
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
7263
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
7264
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
7265
        imgButton = ooButton;
7266
    }
7267
 
7268
    mLastImage = imgButton;
7269
    mChanged = false;
7270
 
7271
    if (!prg_stopped && visible && _displayButton)
7272
    {
7273
        int rwidth = wt;
7274
        int rheight = ht;
7275
        int rleft = mPosLeft;
7276
        int rtop = mPosTop;
7277
#ifdef _SCALE_SKIA_
7278
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
7279
        {
7280
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
7281
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
7282
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
7283
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
7284
 
7285
            SkPaint paint;
7286
            paint.setBlendMode(SkBlendMode::kSrc);
7287
            paint.setFilterQuality(kHigh_SkFilterQuality);
7288
            // Calculate new dimension
7289
            SkImageInfo info = imgButton.info();
7290
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
7291
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
7292
            // Create a canvas and draw new image
7293
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
7294
            imgButton.allocN32Pixels(width, height);
7295
            imgButton.eraseColor(SK_ColorTRANSPARENT);
7296
            SkCanvas can(imgButton, SkSurfaceProps());
7297
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
7298
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
7299
            mLastImage = imgButton;
7300
        }
7301
#endif
7302
        TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
7303
        _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
7304
    }
7305
 
7306
    return true;
7307
}
7308
 
7309
bool TButton::drawJoystickCursor(SkBitmap *bm, int x, int y)
7310
{
7311
    DECL_TRACER("TButton::drawJoystickCursor(SkBitmap *bm, int x, int y)");
7312
 
7313
    if (cd.empty())
7314
        return true;
7315
 
7316
    SkBitmap cursor = drawCursorButton(cd, TColor::getSkiaColor(cc));
7317
 
7318
    if (cursor.empty())
7319
        return false;
7320
 
7321
    SkPaint paint;
7322
    paint.setBlendMode(SkBlendMode::kSrcOver);
7323
    SkCanvas can(*bm, SkSurfaceProps());
7324
 
7325
    int imgWidth = cursor.info().width();
7326
    int imgHeight = cursor.info().height();
7327
 
7328
    int startX = static_cast<int>(static_cast<double>(wt) / static_cast<double>(rh - rl) * static_cast<double>(x));
7329
    int startY = static_cast<int>(static_cast<double>(ht) / static_cast<double>(rh - rl) * static_cast<double>(y));
7330
 
7331
    startX -= imgWidth / 2;
7332
    startY -= imgHeight / 2;
7333
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(cursor);
7334
    can.drawImage(_image, startX, startY, SkSamplingOptions(), &paint);
7335
    return true;
7336
}
7337
 
7338
SkBitmap TButton::drawCursorButton(const string &cursor, SkColor col)
7339
{
7340
    DECL_TRACER("TButton::drawCursorButton(const string &cursor, SkColor col)");
7341
 
7342
    SkBitmap slButton;
7343
    // First we look for the cursor button.
7344
    if (!gPageManager || !gPageManager->getSystemDraw()->existCursor(cursor))
7345
        return slButton;
7346
 
7347
    // There exists one with the wanted name. We grab it and create
7348
    // the images from the files.
7349
    CURSOR_STYLE_t cst;
7350
 
7351
    if (!gPageManager->getSystemDraw()->getCursor(cursor, &cst))    // should never be true!
7352
    {
7353
        MSG_ERROR("No cursor entry found!");
7354
        return slButton;
7355
    }
7356
 
7357
    // Retrieve all available cursor graphics files from the system
7358
    CURSOR_t curFiles = gPageManager->getSystemDraw()->getCursorFiles(cst);
7359
 
7360
    if (curFiles.imageBase.empty() && curFiles.imageAlpha.empty())
7361
    {
7362
        MSG_ERROR("No system cursor graphics found!");
7363
        return SkBitmap();
7364
    }
7365
 
7366
    // Load the images
7367
    SkBitmap imageBase, imageAlpha;
7368
    int width = 0;
7369
    int height = 0;
7370
    bool haveBaseImage = false;
7371
 
7372
    if (!curFiles.imageBase.empty())
7373
    {
7374
        if (!retrieveImage(curFiles.imageBase, &imageBase))
7375
        {
7376
            MSG_ERROR("Unable to load image file " << baseName(curFiles.imageBase));
7377
            return SkBitmap();
7378
        }
7379
 
7380
        width = imageBase.info().width();
7381
        height = imageBase.info().height();
7382
        haveBaseImage = true;
7383
        MSG_DEBUG("Found base image file " << cursor << ".png");
7384
    }
7385
 
7386
    if (!curFiles.imageAlpha.empty())
7387
    {
7388
        if (!retrieveImage(curFiles.imageAlpha, &imageAlpha))
7389
        {
7390
            MSG_ERROR("Unable to load image file " << baseName(curFiles.imageAlpha));
7391
            return SkBitmap();
7392
        }
7393
 
7394
        MSG_DEBUG("Found alpha image file " << cursor << "_alpha.png");
7395
 
7396
        if (!haveBaseImage)
7397
        {
7398
            width = imageAlpha.info().width();
7399
            height = imageAlpha.info().height();
7400
 
7401
            if (!allocPixels(width, height, &imageBase))
7402
                return imageBase;
7403
 
7404
            imageBase.eraseColor(col);
7405
        }
7406
    }
7407
 
7408
    if (imageAlpha.empty())
7409
    {
7410
        MSG_ERROR("Missing alpha mask!");
7411
        return imageAlpha;
7412
    }
7413
 
7414
    SkPaint paint;
7415
    paint.setBlendMode(SkBlendMode::kSrcOver);
7416
 
7417
    if (!allocPixels(width, height, &slButton))
7418
        return slButton;
7419
 
7420
    /*
7421
     * The base image, if it exists, contains the final white mask who must be
7422
     * on top of the image stack. The stack looks like:
7423
     *      alpha image  (top)
7424
     *      base image
7425
     *      target image (bottom)
7426
     * where the "target" image is the one where the others are mapped to.
7427
     *
7428
     * The alpha image contains the cursor in black and white. If there is a
7429
     * base image all visible pixels of the base image must be set to the
7430
     * cursor color by preventing the original alpha value. All other pixels
7431
     * must be marked transparent.
7432
     */
7433
    slButton.eraseColor(SK_ColorTRANSPARENT);
7434
    SkCanvas slCan(slButton, SkSurfaceProps());
7435
 
7436
    if (!haveBaseImage)
7437
    {
7438
        for (int x = 0; x < width; ++x)
7439
        {
7440
            for (int y = 0; y < height; ++y)
7441
            {
7442
                uint32_t *pix = imageBase.getAddr32(x, y);
7443
                SkColor color = imageAlpha.getColor(x, y);
7444
                SkColor alpha = SkColorGetA(color);
7445
 
7446
                if (!alpha)
7447
                    *pix = SK_ColorTRANSPARENT;
7448
            }
7449
        }
7450
    }
7451
    else
7452
    {
7453
        // Colorize alpha image
7454
        for (int x = 0; x < width; ++x)
7455
        {
7456
            for (int y = 0; y < height; ++y)
7457
            {
7458
                uint32_t *pix = imageAlpha.getAddr32(x, y);
7459
                SkColor alpha = SkColorGetA(imageAlpha.getColor(x, y));
7460
 
7461
                if (!alpha)
7462
                {
7463
                    *pix = SK_ColorTRANSPARENT;
7464
                    continue;
7465
                }
7466
 
7467
                if (isBigEndian())
7468
                    *pix = SkColorSetA(col, alpha);
7469
                else
7470
                {
7471
                    SkColor red = SkColorGetR(col);
7472
                    SkColor green = SkColorGetG(col);
7473
                    SkColor blue = SkColorGetB(col);
7474
                    *pix = SkColorSetARGB(alpha, blue, green, red);
7475
                }
7476
            }
7477
        }
7478
    }
7479
 
7480
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imageAlpha);
7481
    slCan.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
7482
    _image = SkImages::RasterFromBitmap(imageBase);
7483
    slCan.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
7484
    return slButton;
7485
}
7486
 
7487
bool TButton::drawList(bool show)
7488
{
7489
    DECL_TRACER("TButton::drawList(bool show)");
7490
 
7491
    if (!mChanged)
7492
    {
7493
        showLastButton();
7494
        return true;
7495
    }
7496
 
7497
    getDrawOrder(sr[0]._do, (DRAW_ORDER *)&mDOrder);
7498
 
7499
    if (TError::isError())
7500
        return false;
7501
 
7502
    SkBitmap imgButton;
7503
 
7504
    if (!allocPixels(wt, ht, &imgButton))
7505
        return false;
7506
 
7507
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
7508
    {
7509
        if (mDOrder[i] == ORD_ELEM_FILL)
7510
        {
7511
            if (!buttonFill(&imgButton, 0))
7512
                return false;
7513
        }
7514
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
7515
        {
7516
            if (!sr[0].dynamic && !buttonBitmap(&imgButton, 0))
7517
                return false;
7518
            else if (sr[0].dynamic && !buttonDynamic(&imgButton, 0, false))
7519
                return false;
7520
        }
7521
        else if (mDOrder[i] == ORD_ELEM_ICON)
7522
        {
7523
            if (!buttonIcon(&imgButton, 0))
7524
                return false;
7525
        }
7526
        else if (mDOrder[i] == ORD_ELEM_BORDER)
7527
        {
7528
            if (!buttonBorder(&imgButton, 0))
7529
                return false;
7530
        }
7531
    }
7532
 
7533
    if (mGlobalOO >= 0 || sr[0].oo >= 0) // Take overall opacity into consideration
7534
    {
7535
        SkBitmap ooButton;
7536
        int w = imgButton.width();
7537
        int h = imgButton.height();
7538
 
7539
        if (!allocPixels(w, h, &ooButton))
7540
            return false;
7541
 
7542
        SkCanvas canvas(ooButton);
7543
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
7544
        SkRegion region;
7545
        region.setRect(irect);
7546
        SkScalar oo;
7547
 
7548
        if (mGlobalOO >= 0 && sr[0].oo >= 0)
7549
        {
7550
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[0].oo);
7551
            MSG_DEBUG("Set global overal opacity to " << oo);
7552
        }
7553
        else if (sr[0].oo >= 0)
7554
        {
7555
            oo = (SkScalar)sr[0].oo;
7556
            MSG_DEBUG("Set overal opacity to " << oo);
7557
        }
7558
        else
7559
        {
7560
            oo = (SkScalar)mGlobalOO;
7561
            MSG_DEBUG("Set global overal opacity to " << oo);
7562
        }
7563
 
7564
        SkScalar alpha = 1.0 / 255.0 * oo;
7565
        MSG_DEBUG("Calculated alpha value: " << alpha);
7566
        SkPaint paint;
7567
        paint.setAlphaf(alpha);
7568
//        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
7569
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
7570
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
7571
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
7572
        imgButton = ooButton;
7573
    }
7574
 
7575
    mLastImage = imgButton;
7576
    mChanged = false;
7577
 
7578
    if (!prg_stopped)
7579
    {
7580
        int rwidth = wt;
7581
        int rheight = ht;
7582
        int rleft = mPosLeft;
7583
        int rtop = mPosTop;
7584
        size_t rowBytes = imgButton.info().minRowBytes();
7585
#ifdef _SCALE_SKIA_
7586
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
7587
        {
7588
            size_t rowBytes = imgButton.info().minRowBytes();
7589
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
7590
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
7591
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
7592
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
7593
 
7594
            SkPaint paint;
7595
            paint.setBlendMode(SkBlendMode::kSrc);
7596
            paint.setFilterQuality(kHigh_SkFilterQuality);
7597
            // Calculate new dimension
7598
            SkImageInfo info = imgButton.info();
7599
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
7600
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
7601
            // Create a canvas and draw new image
7602
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
7603
            imgButton.allocN32Pixels(width, height);
7604
            imgButton.eraseColor(SK_ColorTRANSPARENT);
7605
            SkCanvas can(imgButton, SkSurfaceProps());
7606
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
7607
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
7608
            rowBytes = imgButton.info().minRowBytes();
7609
            mLastImage = imgButton;
7610
        }
7611
#endif
7612
        if (show && gPageManager && gPageManager->getCallbackListBox())
7613
        {
7614
            BITMAP_t bm;
7615
            bm.buffer = (unsigned char *)imgButton.getPixels();
7616
            bm.rowBytes = rowBytes;
7617
            bm.left = rleft;
7618
            bm.top = rtop;
7619
            bm.width = rwidth;
7620
            bm.height = rheight;
7621
            gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
7622
        }
7623
    }
7624
 
7625
    return true;
7626
}
7627
 
7628
bool TButton::drawBargraph(int instance, int level, bool show)
7629
{
7630
    DECL_TRACER("TButton::drawBargraph(int instance, int level, bool show)");
7631
 
7632
    if ((size_t)instance >= sr.size() || instance < 0)
7633
    {
7634
        MSG_ERROR("Instance " << instance << " is out of bounds!");
7635
        TError::setError();
7636
        return false;
7637
    }
7638
 
7639
    if (!_displayButton && gPageManager)
7640
        _displayButton = gPageManager->getCallbackDB();
7641
 
7642
    TButtonStates *buttonStates = getButtonState();
7643
 
7644
    if (!buttonStates)
7645
    {
7646
        MSG_ERROR("Button states not found!");
7647
        return false;
7648
    }
7649
 
7650
    int lastLevel = buttonStates->getLastLevel();
7651
 
7652
    if (!mChanged && lastLevel == level)
7653
    {
7654
        MSG_DEBUG("Drawing unchanged button with level " << level);
7655
        showLastButton();
7656
        return true;
7657
    }
7658
 
7659
    if (level < rl)
7660
        lastLevel = rl;
7661
    else if (level > rh)
7662
        lastLevel = rh;
7663
    else
7664
        lastLevel = level;
7665
 
7666
    buttonStates->setLastLevel(lastLevel);
7667
    int inst = instance;
7668
    MSG_DEBUG("drawing bargraph " << lp << ":" << lv << " with level " << lastLevel << " at instance " << inst);
7669
 
7670
    if (!visible || hd || instance != mActInstance || !_displayButton)
7671
    {
7672
        bool db = (_displayButton != nullptr);
7673
        MSG_DEBUG("Bargraph " << bi << ", \"" << na << "\" at instance " << instance << " with level " << lastLevel << " is not to draw!");
7674
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
7675
        return true;
7676
    }
7677
 
7678
    ulong parent = mHandle & 0xffff0000;
7679
 
7680
    if (type == BARGRAPH)
7681
    {
7682
        getDrawOrder(sr[1]._do, (DRAW_ORDER *)&mDOrder);
7683
        inst = 1;
7684
    }
7685
    else
7686
        getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
7687
 
7688
    if (TError::isError())
7689
        return false;
7690
 
7691
    SkBitmap imgButton;
7692
 
7693
    if (!allocPixels(wt, ht, &imgButton))
7694
        return false;
7695
 
7696
    imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
7697
    bool haveFrame = false;
7698
 
7699
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
7700
    {
7701
        if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
7702
        {
7703
            if (!buttonFill(&imgButton, (type == BARGRAPH ? 0 : inst)))
7704
                return false;
7705
        }
7706
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
7707
        {
7708
            if (!barLevel(&imgButton, inst, lastLevel))
7709
                return false;
7710
        }
7711
        else if (mDOrder[i] == ORD_ELEM_ICON)
7712
        {
7713
            if (!buttonIcon(&imgButton, inst))
7714
                return false;
7715
        }
7716
        else if (mDOrder[i] == ORD_ELEM_TEXT)
7717
        {
7718
            if (!buttonText(&imgButton, inst))
7719
                return false;
7720
        }
7721
        else if (mDOrder[i] == ORD_ELEM_BORDER)
7722
        {
7723
            if (!buttonBorder(&imgButton, (type == BARGRAPH ? 0 : inst)))
7724
                return false;
7725
 
7726
            haveFrame = true;
7727
        }
7728
    }
7729
 
7730
    if (mGlobalOO >= 0 || sr[inst].oo >= 0) // Take overall opacity into consideration
7731
    {
7732
        SkBitmap ooButton;
7733
        int w = imgButton.width();
7734
        int h = imgButton.height();
7735
 
7736
        if (!allocPixels(w, h, &ooButton))
7737
            return false;
7738
 
7739
        SkCanvas canvas(ooButton);
7740
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
7741
        SkRegion region;
7742
        region.setRect(irect);
7743
        SkScalar oo;
7744
 
7745
        if (mGlobalOO >= 0 && sr[inst].oo >= 0)
7746
        {
7747
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[inst].oo);
7748
            MSG_DEBUG("Set global overal opacity to " << oo);
7749
        }
7750
        else if (sr[inst].oo >= 0)
7751
        {
7752
            oo = (SkScalar)sr[inst].oo;
7753
            MSG_DEBUG("Set overal opacity to " << oo);
7754
        }
7755
        else
7756
        {
7757
            oo = (SkScalar)mGlobalOO;
7758
            MSG_DEBUG("Set global overal opacity to " << oo);
7759
        }
7760
 
7761
        SkScalar alpha = 1.0 / 255.0 * oo;
7762
        MSG_DEBUG("Calculated alpha value: " << alpha);
7763
        SkPaint paint;
7764
        paint.setAlphaf(alpha);
7765
//        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
7766
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
7767
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
7768
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
7769
        imgButton = ooButton;
7770
    }
7771
 
7772
    mLastImage = imgButton;
7773
    mChanged = false;
7774
 
7775
    if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
7776
    {
7777
        int rwidth = wt;
7778
        int rheight = ht;
7779
        int rleft = mPosLeft;
7780
        int rtop = mPosTop;
7781
#ifdef _SCALE_SKIA_
7782
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
7783
        {
7784
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
7785
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
7786
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
7787
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
7788
 
7789
            SkPaint paint;
7790
            paint.setBlendMode(SkBlendMode::kSrc);
7791
            paint.setFilterQuality(kHigh_SkFilterQuality);
7792
            // Calculate new dimension
7793
            SkImageInfo info = imgButton.info();
7794
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
7795
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
7796
            // Create a canvas and draw new image
7797
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
7798
            imgButton.allocN32Pixels(width, height);
7799
            imgButton.eraseColor(SK_ColorTRANSPARENT);
7800
            SkCanvas can(imgButton, SkSurfaceProps());
7801
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
7802
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
7803
            mLastImage = imgButton;
7804
        }
7805
#endif
7806
        TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
7807
        _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
7808
    }
7809
 
7810
    return true;
7811
}
7812
 
7813
POSITION_t TButton::calcImagePosition(int width, int height, CENTER_CODE cc, int number, int line)
7814
{
7815
    DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
7816
 
7817
    SR_T act_sr;
7818
    POSITION_t position;
7819
    int ix, iy, ln;
7820
 
7821
    if (sr.size() == 0)
7822
        return position;
7823
 
7824
    if (number <= 0)
7825
        act_sr = sr.at(0);
7826
    else if ((size_t)number < sr.size())
7827
        act_sr = sr.at(number);
7828
    else if ((size_t)number >= sr.size())
7829
        act_sr = sr.at(sr.size() - 1);
7830
    else
7831
        return position;
7832
 
7833
    if (line <= 0)
7834
        ln = 1;
7835
    else
7836
        ln = line;
7837
 
7838
    int border_size = getBorderSize(act_sr.bs);
7839
    int code, border = border_size;
7840
    string dbgCC;
7841
    int rwt = 0, rht = 0;
7842
 
7843
    switch (cc)
7844
    {
7845
        case SC_ICON:
7846
            code = act_sr.ji;
7847
            ix = act_sr.ix;
7848
            iy = act_sr.iy;
7849
            border = border_size = 0;
7850
            dbgCC = "ICON";
7851
            rwt = width;
7852
            rht = height;
7853
        break;
7854
 
7855
        case SC_BITMAP:
7856
            code = act_sr.jb;
7857
            ix = act_sr.bx;
7858
            iy = act_sr.by;
7859
            dbgCC = "BITMAP";
7860
            rwt = std::min(wt - border * 2, width);
7861
            rht = std::min(ht - border_size * 2, height);
7862
        break;
7863
 
7864
        case SC_TEXT:
7865
            code = act_sr.jt;
7866
            ix = act_sr.tx;
7867
            iy = act_sr.ty;
7868
            dbgCC = "TEXT";
7869
 
7870
            if (border < 4)
7871
                border = 4;
7872
 
7873
            rwt = std::min(wt - border * 2, width);         // We've always a minimum (invisible) border of 4 pixels.
7874
            rht = std::min(ht - border_size * 2, height);   // The height is calculated from a defined border, if any.
7875
        break;
7876
    }
7877
 
7878
    if (width > rwt || height > rht)
7879
        position.overflow = true;
7880
 
7881
    switch (code)
7882
    {
7883
        case 0: // absolute position
7884
            position.left = ix;
7885
            position.top = iy;
7886
 
7887
            if (cc == SC_BITMAP && ix < 0 && rwt < width)
7888
                position.left *= -1;
7889
 
7890
            if (cc == SC_BITMAP && iy < 0 && rht < height)
7891
                position.top += -1;
7892
 
7893
            position.width = rwt;
7894
            position.height = rht;
7895
        break;
7896
 
7897
        case 1: // top, left
7898
            if (cc == SC_TEXT)
7899
            {
7900
                position.left = border;
7901
                position.top = border; // ht - ((ht - rht) / 2) - height * ln;
7902
            }
7903
 
7904
            position.width = rwt;
7905
            position.height = rht;
7906
        break;
7907
 
7908
        case 2: // center, top
7909
            if (cc == SC_TEXT)
7910
                position.top = border; // ht - ((ht - rht) / 2) - height * ln;
7911
 
7912
            position.left = (wt - rwt) / 2;
7913
            position.height = rht;
7914
            position.width = rwt;
7915
        break;
7916
 
7917
        case 3: // right, top
7918
            position.left = wt - rwt;
7919
 
7920
            if (cc == SC_TEXT)
7921
            {
7922
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
7923
                position.top = border; // ht - (ht - rht) - height * ln;
7924
            }
7925
 
7926
            position.width = rwt;
7927
            position.height = rht;
7928
        break;
7929
 
7930
        case 4: // left, middle
7931
            if (cc == SC_TEXT)
7932
            {
7933
                position.left = border;
7934
                position.top = (ht - height) / 2;
7935
            }
7936
            else
7937
                position.top = (ht - rht) / 2;
7938
 
7939
            position.width = rwt;
7940
            position.height = rht;
7941
        break;
7942
 
7943
        case 6: // right, middle
7944
            position.left = wt - rwt;
7945
 
7946
            if (cc == SC_TEXT)
7947
            {
7948
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
7949
                position.top = (ht - height) / 2;
7950
            }
7951
            else
7952
                position.top = (ht - rht) / 2;
7953
 
7954
            position.width = rwt;
7955
            position.height = rht;
7956
        break;
7957
 
7958
        case 7: // left, bottom
7959
            if (cc == SC_TEXT)
7960
            {
7961
                position.left = border_size;
7962
                position.top = (ht - rht) - height * ln;
7963
            }
7964
            else
7965
                position.top = ht - rht;
7966
 
7967
            position.width = rwt;
7968
            position.height = rht;
7969
        break;
7970
 
7971
        case 8: // center, bottom
7972
            position.left = (wt - rwt) / 2;
7973
 
7974
            if (cc == SC_TEXT)
7975
                position.top = (ht - rht) - height * ln;
7976
            else
7977
                position.top = ht - rht;
7978
 
7979
            position.width = rwt;
7980
            position.height = rht;
7981
        break;
7982
 
7983
        case 9: // right, bottom
7984
            position.left = wt - rwt;
7985
 
7986
            if (cc == SC_TEXT)
7987
            {
7988
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
7989
                position.top = (ht - rht) - height * ln;
7990
            }
7991
            else
7992
                position.top = ht - rht;
7993
        break;
7994
 
7995
        default: // center, middle
7996
            position.left = (wt - rwt) / 2;
7997
 
7998
            if (cc == SC_TEXT)
7999
                position.top = (ht - height) / 2;
8000
            else
8001
                position.top = (ht - rht) / 2;
8002
 
8003
            position.width = rwt;
8004
            position.height = rht;
8005
    }
8006
 
8007
    if (TStreamError::checkFilter(HLOG_DEBUG))
8008
    {
479 andreas 8009
        string format = getFormatString((ORIENTATION)code);
446 andreas 8010
        MSG_DEBUG("Type: " << dbgCC << ", format: " << format <<
8011
            ", PosType=" << code << ", total height=" << ht << ", height object=" << height <<
8012
            ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width <<
8013
            ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
8014
    }
8015
 
8016
    position.valid = true;
8017
    return position;
8018
}
8019
 
8020
IMAGE_SIZE_t TButton::calcImageSize(int imWidth, int imHeight, int instance, bool aspect)
8021
{
8022
    DECL_TRACER("TButton::calcImageSize(int imWidth, int imHeight, bool aspect)");
8023
 
8024
    int border = getBorderSize(sr[instance].bs);
8025
    IMAGE_SIZE_t isize;
8026
 
8027
    if (!aspect)
8028
    {
8029
        isize.width = wt - border * 2;
8030
        isize.height = ht - border * 2;
8031
    }
8032
    else
8033
    {
8034
        int w = wt - border * 2;
8035
        int h = ht - border * 2;
8036
        double scale;
8037
 
8038
        if (w < h || imWidth > imHeight)
8039
            scale = (double)w / (double)imWidth;
8040
        else
8041
            scale = (double)h / (double)imHeight;
8042
 
8043
        isize.width = (int)((double)imWidth * scale);
8044
        isize.height = (int)((double)imHeight * scale);
8045
    }
8046
 
8047
    MSG_DEBUG("Sizing image: Original: " << imWidth << " x " << imHeight << " to " << isize.width << " x " << isize.height);
8048
    return isize;
8049
}
8050
 
479 andreas 8051
string TButton::getFormatString(ORIENTATION to)
446 andreas 8052
{
8053
    DECL_TRACER("TButton::getFormatString(CENTER_CODE cc)");
8054
 
8055
    switch(to)
8056
    {
8057
        case ORI_ABSOLUT:       return "ABSOLUT";
8058
        case ORI_BOTTOM_LEFT:   return "BOTTOM/LEFT";
8059
        case ORI_BOTTOM_MIDDLE: return "BOTTOM/MIDDLE";
8060
        case ORI_BOTTOM_RIGHT:  return "BOTTOM/RIGHT";
8061
        case ORI_CENTER_LEFT:   return "CENTER/LEFT";
8062
        case ORI_CENTER_MIDDLE: return "CENTER/MIDDLE";
8063
        case ORI_CENTER_RIGHT:  return "CENTER/RIGHT";
8064
        case ORI_TOP_LEFT:      return "TOP/LEFT";
8065
        case ORI_TOP_MIDDLE:    return "TOP/MIDDLE";
8066
        case ORI_TOP_RIGHT:     return "TOP/RIGHT";
482 andreas 8067
        case ORI_SCALE_FIT:     return "SCALE/FIT";
8068
        case ORI_SCALE_ASPECT:  return "SCALE/ASPECT";
446 andreas 8069
    }
8070
 
8071
    return "UNKNOWN";   // Should not happen!
8072
}
8073
 
8074
int TButton::getBorderSize(const std::string& name)
8075
{
8076
    DECL_TRACER("TButton::getBorderSize(const std::string& name)");
8077
 
8078
    int width = getBorderWidth(name);
8079
 
8080
    if (width > 0)
8081
        return width;
8082
 
8083
    if (gPageManager && gPageManager->getSystemDraw())
8084
    {
8085
        if (gPageManager->getSystemDraw()->existBorder(name))
8086
            return gPageManager->getSystemDraw()->getBorderWidth(name);
8087
    }
8088
 
8089
    return 0;
8090
}
8091
 
8092
void TButton::setUserName(const string& user)
8093
{
8094
    DECL_TRACER("TButton::setUserName(const string& user)");
8095
 
8096
    if (TConfig::getUserPassword(user).empty())
8097
        return;
8098
 
8099
    mUser = user;
8100
}
8101
 
8102
void TButton::calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)
8103
{
8104
    DECL_TRACER("TButton::clacImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)");
8105
 
8106
    int spX = btWidth - (btFrame * 2);
8107
    int spY = btHeight - (btFrame * 2);
8108
 
8109
    if (imWidth <= spX && imHeight <= spY)
8110
    {
8111
        *realX = imWidth;
8112
        *realY = imHeight;
8113
        return;
8114
    }
8115
 
8116
    int oversizeX = 0, oversizeY = 0;
8117
 
8118
    if (imWidth > spX)
8119
        oversizeX = imWidth - spX;
8120
 
8121
    if (imHeight > spY)
8122
        oversizeY = imHeight - spY;
8123
 
8124
    double percent = 0.0;
8125
 
8126
    if (oversizeX > oversizeY)
8127
        percent = 100.0 / (double)imWidth * (double)spX;
8128
    else
8129
        percent = 100.0 / (double)imHeight * (double)spY;
8130
 
8131
    *realX = (int)(percent / 100.0 * (double)imWidth);
8132
    *realY = (int)(percent / 100.0 * (double)imHeight);
8133
}
8134
 
8135
SkBitmap TButton::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
8136
{
8137
    DECL_TRACER("TButton::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
8138
 
8139
    if (width <= 0 || height <= 0)
8140
    {
8141
        MSG_WARNING("Got invalid width of height! (width: " << width << ", height: " << height << ")");
8142
        return SkBitmap();
8143
    }
8144
 
8145
    if (imgRed.empty())
8146
    {
8147
        MSG_WARNING("Missing mask to draw image!");
8148
        return SkBitmap();
8149
    }
8150
 
8151
    SkPixmap pixmapRed = imgRed.pixmap();
8152
    SkPixmap pixmapMask;
8153
    bool haveBothImages = true;
8154
 
8155
    if (!imgMask.empty())
8156
        pixmapMask = imgMask.pixmap();
8157
    else
8158
        haveBothImages = false;
8159
 
8160
    SkBitmap maskBm;
8161
 
8162
    if (!allocPixels(width, height, &maskBm))
8163
        return SkBitmap();
8164
 
8165
    maskBm.eraseColor(SK_ColorTRANSPARENT);
8166
 
8167
    for (int ix = 0; ix < width; ix++)
8168
    {
8169
        for (int iy = 0; iy < height; iy++)
8170
        {
8171
            SkColor pixelRed;
8172
            SkColor pixelMask;
8173
 
8174
            if (ix < pixmapRed.info().width() && iy < pixmapRed.info().height())
8175
                pixelRed = pixmapRed.getColor(ix, iy);
8176
            else
8177
                pixelRed = 0;
8178
 
8179
            if (haveBothImages && !imgMask.empty() &&
8180
                    ix < pixmapMask.info().width() && iy < pixmapMask.info().height())
8181
                pixelMask = pixmapMask.getColor(ix, iy);
8182
            else
8183
                pixelMask = SkColorSetA(SK_ColorWHITE, 0);
8184
 
8185
            SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
8186
            uint32_t alpha = SkColorGetA(pixel);
8187
            uint32_t *wpix = nullptr;
8188
 
8189
            if (ix < maskBm.info().width() && iy < maskBm.info().height())
8190
                wpix = maskBm.getAddr32(ix, iy);
8191
 
8192
            if (!wpix)
8193
                continue;
8194
 
8195
            if (alpha == 0)
8196
                pixel = pixelMask;
8197
 
8198
            *wpix = pixel;
8199
        }
8200
    }
8201
 
8202
    return maskBm;
8203
}
8204
 
8205
/**
8206
 * @brief Takes 2 images and combines them to one.
8207
 *
8208
 * The 2 images are a solid base image defining the basic form and an identical
8209
 * image defining the alpha channel.
8210
 *
8211
 * @param base  The base image containing the form as black pixels.
8212
 * @param alpha The image containing just an alpha channel.
8213
 * @param col   The color which should be used instead of a black pixel.
8214
 *
8215
 * @return On success a valid bitmap is returned containing the form.
8216
 * On error an empty bitmap is returned.
8217
 */
8218
SkBitmap TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)
8219
{
8220
    DECL_TRACER("TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)");
8221
 
8222
    int width = base.info().width();
8223
    int height = base.info().height();
8224
    SkBitmap Bm;    // The new bitmap. It will be returned in the end.
8225
 
8226
    if (width != alpha.info().width() || height != alpha.info().height())
8227
    {
8228
        MSG_ERROR("Mask and alpha have different size! [ " << width << " x " << height << " to " << alpha.info().width() << " x " << alpha.info().height());
8229
        return Bm;
8230
    }
8231
 
8232
    if (!allocPixels(width, height, &Bm))
8233
        return Bm;
8234
 
8235
    Bm.eraseColor(SK_ColorTRANSPARENT);
8236
 
8237
    for (int ix = 0; ix < width; ix++)
8238
    {
8239
        for (int iy = 0; iy < height; iy++)
8240
        {
8241
            SkColor pixelAlpha = alpha.getColor(ix, iy);
8242
            uint32_t *bpix = Bm.getAddr32(ix, iy);
8243
 
8244
            uchar al    = SkColorGetA(pixelAlpha);
8245
            uchar red   = SkColorGetR(col);
8246
            uchar green = SkColorGetG(col);
8247
            uchar blue  = SkColorGetB(col);
8248
 
8249
            if (pixelAlpha == 0)
8250
                red = green = blue = 0;
8251
 
8252
            // Skia reads image files in the natural byte order of the CPU.
8253
            // While on Intel CPUs the byte order is little endian it is
8254
            // mostly big endian on other CPUs. This means that the order of
8255
            // the colors is RGB on big endian CPUs (ARM, ...) and BGR on others.
8256
            // To compensate this, we check the endianess of the CPU and set
8257
            // the byte order according.
8258
 
8259
            if (isBigEndian())
8260
                *bpix = SkColorSetARGB(al, blue, green, red);
8261
            else
8262
                *bpix = SkColorSetARGB(al, red, green, blue);
8263
        }
8264
    }
8265
 
8266
    SkPaint paint;
8267
    paint.setBlendMode(SkBlendMode::kSrcOver);
8268
    SkCanvas can(Bm);
8269
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(base);
8270
    can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
8271
    return Bm;
8272
}
8273
 
8274
/**
8275
 * @brief TButton::colorImage: Colorize frame element
8276
 * This method colorizes a frame element. If there is, beside the base picture,
8277
 * also a an alpha mask picture present, the elemnt is colorized by taking the
8278
 * mask to find the pixels to colorize.
8279
 * Otherwise the pixel is melted with the target color. This means a pseudo mask
8280
 * is used.
8281
 *
8282
 * @param base      This is the base image and must be present.
8283
 * @param alpha     This is optional alpha mask. If present it is used to
8284
 *                  define the alpha value of the pixels.
8285
 * @param col       This is the color to be used.
8286
 * @param bg        This is the background color to be used on the transparent
8287
 *                  pixels inside an element. On the transparent pixels on the
8288
 *                  outside of the element the pixel is set to transparent.
8289
 * @param useBG     If this is TRUE, all transparent pixels are set to the
8290
 *                  background color \b bg.
8291
 * @return
8292
 * On success a new image containing the colorized element is returned.
8293
 * Otherwise an empty image is returned.
8294
 */
8295
SkBitmap TButton::colorImage(SkBitmap& base, SkBitmap& alpha, SkColor col, SkColor bg, bool useBG)
8296
{
8297
    DECL_TRACER("TButton::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)");
8298
 
8299
    int width = base.info().width();
8300
    int height = base.info().height();
8301
 
8302
    if (width <= 0 || height <= 0)
8303
    {
8304
        MSG_WARNING("Got invalid width or height! (width: " << width << ", height: " << height << ")");
8305
        return SkBitmap();
8306
    }
8307
 
8308
    if (!alpha.empty())
8309
    {
8310
        if (width != alpha.info().width() || height != alpha.info().height())
8311
        {
8312
            MSG_ERROR("Base and alpha masks have different size!");
8313
            return SkBitmap();
8314
        }
8315
    }
8316
 
8317
    SkBitmap maskBm;
8318
 
8319
    if (!allocPixels(width, height, &maskBm))
8320
        return SkBitmap();
8321
 
8322
    maskBm.eraseColor(SK_ColorTRANSPARENT);
8323
 
8324
    for (int ix = 0; ix < width; ix++)
8325
    {
8326
        for (int iy = 0; iy < height; iy++)
8327
        {
8328
            SkColor pixelAlpha = 0;
8329
 
8330
            if (!alpha.empty())
8331
                pixelAlpha = alpha.getColor(ix, iy);
8332
            else
8333
                pixelAlpha = base.getColor(ix, iy);
8334
 
8335
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
8336
 
8337
            if (!wpix)
8338
            {
8339
                MSG_ERROR("No pixel buffer!");
8340
                break;
8341
            }
8342
 
8343
            uint32_t ala = SkColorGetA(pixelAlpha);
8344
 
8345
            if (ala == 0 && !useBG)
8346
                pixelAlpha = SK_ColorTRANSPARENT;
8347
            else if (ala == 0)
8348
                pixelAlpha = bg;
8349
            else
8350
            {
8351
                uint32_t red   = SkColorGetR(col);
8352
                uint32_t green = SkColorGetG(col);
8353
                uint32_t blue  = SkColorGetB(col);
8354
                pixelAlpha = SkColorSetARGB(ala, red, green, blue);
8355
            }
8356
 
8357
            *wpix = pixelAlpha;
8358
        }
8359
    }
8360
 
8361
    if (!alpha.empty())
8362
    {
8363
        SkPaint paint;
8364
        paint.setBlendMode(SkBlendMode::kSrcOver);
8365
        SkCanvas can(maskBm);
8366
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(base);
8367
        can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
8368
    }
8369
 
8370
    return maskBm;
8371
}
8372
 
8373
bool TButton::retrieveImage(const string& path, SkBitmap* image)
8374
{
8375
    DECL_TRACER("TButton::retrieveImage(const string& path, SkBitmap* image)");
8376
 
8377
    if (path.empty() || !image)
8378
    {
8379
        MSG_WARNING("TButton::retrieveImage: Empty parameter!");
8380
        return false;
8381
    }
8382
 
8383
    if (!fs::exists(path) || !fs::is_regular_file(path))
8384
    {
8385
        MSG_WARNING("File \"" << path << "\" does not exist or is not a regular file!");
8386
        return false;
8387
    }
8388
 
8389
    sk_sp<SkData> im;
8390
 
8391
    if (!(im = readImage(path)))
8392
        return false;
8393
 
8394
    DecodeDataToBitmap(im, image);
8395
 
8396
    if (image->empty())
8397
    {
8398
        MSG_WARNING("Could not create the image " << path);
8399
        return false;
8400
    }
8401
 
8402
    return true;
8403
}
8404
 
8405
/**
8406
 * @brief getBorderFragment - get part of border
8407
 * The method reads a border image fragment from the disk and converts it to
8408
 * the border color. If there is a base image and an alpha mask image, the
8409
 * pixels of the alpha mask are converted to the border color and then the base
8410
 * image is layed over the mask image.
8411
 * In case there is no base image, an image with the same size as the mask image
8412
 * is created and filled transparaent.
8413
 *
8414
 * @param path      The path and file name of the base image.
8415
 * @param pathAlpha The path and file name of the alpha mask image.
8416
 * @param image     A pointer to an empty bitmap.
8417
 * @param color     The border color
8418
 *
8419
 * @return In case the images exists and were loaded successfully, TRUE is
8420
 * returned.
8421
 */
8422
bool TButton::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)
8423
{
8424
    DECL_TRACER("TButton::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)");
8425
 
8426
    if (!image)
8427
    {
8428
        MSG_ERROR("Invalid pointer to image!");
8429
        return false;
8430
    }
8431
 
8432
    sk_sp<SkData> im;
8433
    SkBitmap bm;
8434
    bool haveBaseImage = false;
8435
    SkColor swCol = color;
8436
 
8437
    if (!isBigEndian())
8438
        flipColorLevelsRB(swCol);
8439
 
8440
    // If the path ends with "alpha.png" then it is a mask image. This not what
8441
    // we want first unless this is the only image available.
8442
    if (!endsWith(path, "alpha.png") || pathAlpha.empty())
8443
    {
8444
        if (!path.empty() && retrieveImage(path, image))
8445
        {
8446
            haveBaseImage = true;
8447
            // Underly the pixels with the border color
8448
            MSG_DEBUG("Path: " << path << ", pathAlpha: " << pathAlpha);
8449
            if (pathAlpha.empty() || !fs::exists(pathAlpha) || path == pathAlpha)
8450
            {
8451
                SkImageInfo info = image->info();
8452
                SkBitmap b;
8453
                allocPixels(info.width(), info.height(), &b);
8454
                b.eraseColor(SK_ColorTRANSPARENT);
8455
 
8456
                for (int x = 0; x < info.width(); ++x)
8457
                {
8458
                    for (int y = 0; y < info.height(); ++y)
8459
                    {
8460
                        SkColor alpha = SkColorGetA(image->getColor(x, y));
8461
                        uint32_t *pix = b.getAddr32(x, y);
8462
 
8463
                        if (alpha > 0)
8464
                            *pix = swCol;
8465
                    }
8466
                }
8467
 
8468
                SkPaint paint;
8469
                paint.setAntiAlias(true);
8470
                paint.setBlendMode(SkBlendMode::kDstATop);
8471
                SkCanvas can(*image);
8472
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(b);
8473
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
8474
            }
8475
        }
8476
    }
8477
 
8478
    // If there is no valid path return.
8479
    if (pathAlpha.empty())
8480
        return haveBaseImage;
8481
 
8482
    // On error retrieving the image, return.
8483
    if (!retrieveImage(pathAlpha, &bm))
8484
        return haveBaseImage;
8485
 
8486
    // If there was no base image loaded, allocate the space for an image
8487
    // filled transparent. Make it the same size as the mask image.
8488
    if (!haveBaseImage)
8489
    {
8490
        allocPixels(bm.info().width(), bm.info().height(), image);
8491
        image->eraseColor(SK_ColorTRANSPARENT);
8492
    }
8493
 
8494
    // Only if the base image and the mask image have the same size, which
8495
    // should be the case, then the visible pixels of the mask image are
8496
    // colored by the border color.
8497
    if (image->info().dimensions() == bm.info().dimensions())
8498
    {
8499
        for (int y = 0; y < image->info().height(); ++y)
8500
        {
8501
            for (int x = 0; x < image->info().width(); ++x)
8502
            {
8503
                SkColor col = bm.getColor(x, y);
8504
                SkColor alpha = SkColorGetA(col);
8505
                uint32_t *pix = bm.getAddr32(x, y);
8506
 
8507
                if (alpha == 0)
8508
                    *pix = SK_ColorTRANSPARENT;
8509
                else
8510
                    *pix = SkColorSetA(swCol, alpha);
8511
            }
8512
        }
8513
    }
8514
 
8515
    // Here we draw the border fragment over the base image.
8516
    SkPaint paint;
8517
    paint.setAntiAlias(true);
8518
    paint.setBlendMode(SkBlendMode::kDstATop);
8519
    SkCanvas can(*image);
8520
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bm);
8521
    can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
8522
 
8523
    return true;
8524
}
8525
 
8526
void TButton::show()
8527
{
8528
    DECL_TRACER("TButton::show()");
8529
 
8530
    // First we detect whether we have a dynamic button or not.
8531
    // To do this, we find out the current active instance.
8532
    int inst = 0;
8533
 
8534
    if (mActInstance >= 0 && (size_t)mActInstance < sr.size())
8535
        inst = mActInstance;
8536
    // If the dynamic flag is not set and we have already an image of the
8537
    // button, we send just the saved image to the screen.
8538
    if (visible && !mChanged && !sr[inst].dynamic && !mLastImage.empty())
8539
    {
8540
        showLastButton();
8541
        return;
8542
    }
8543
    // Here the button, or the active instance was never drawn or it is a
8544
    // dynamic button. Then the button must be drawn.
8545
    visible = true;
8546
    makeElement();
8547
 
8548
    if (isSystemButton() && !mSystemReg)
8549
        registerSystemButton();
8550
}
8551
 
8552
void TButton::showLastButton()
8553
{
8554
    DECL_TRACER("TButton::showLastButton()");
8555
 
8556
    if (mLastImage.empty())
8557
    {
8558
#if TESTMODE == 1
8559
        setScreenDone();
8560
#endif
8561
        return;
8562
    }
8563
 
8564
    if (!_displayButton && gPageManager)
8565
        _displayButton = gPageManager->getCallbackDB();
8566
 
8567
    if (!prg_stopped && visible)
8568
    {
8569
        ulong parent = mHandle & 0xffff0000;
8570
        size_t rowBytes = mLastImage.info().minRowBytes();
8571
        int rwidth = wt;
8572
        int rheight = ht;
8573
        int rleft = mPosLeft;
8574
        int rtop = mPosTop;
8575
#ifdef _SCALE_SKIA_
8576
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
8577
        {
8578
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
8579
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
8580
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
8581
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
8582
        }
8583
#endif
8584
        if (type == TEXT_INPUT)
8585
        {
8586
            if (gPageManager && gPageManager->getCallbackInputText())
8587
            {
8588
                BITMAP_t bm;
8589
                bm.buffer = (unsigned char *)mLastImage.getPixels();
8590
                bm.rowBytes = rowBytes;
8591
                bm.left = rleft;
8592
                bm.top = rtop;
8593
                bm.width = rwidth;
8594
                bm.height = rheight;
8595
                gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
8596
            }
8597
        }
8598
        else if (type == LISTBOX)
8599
        {
8600
            if (gPageManager && gPageManager->getCallbackListBox())
8601
            {
8602
                BITMAP_t bm;
8603
                bm.buffer = (unsigned char *)mLastImage.getPixels();
8604
                bm.rowBytes = rowBytes;
8605
                bm.left = rleft;
8606
                bm.top = rtop;
8607
                bm.width = rwidth;
8608
                bm.height = rheight;
8609
                gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
8610
            }
8611
        }
8612
        else if (type == SUBPAGE_VIEW)
8613
        {
8614
            if (gPageManager && gPageManager->getDisplayViewButton())
8615
            {
8616
                TBitmap image((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
8617
                TColor::COLOR_T bgcolor = TColor::getAMXColor(sr[mActInstance].cf);
8618
                gPageManager->getDisplayViewButton()(mHandle, getParent(), (on.empty() ? false : true), image, wt, ht, mPosLeft, mPosTop, sa, bgcolor);
8619
            }
8620
        }
8621
        else if (_displayButton)
8622
        {
8623
            TBitmap image((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
8624
            _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
8625
 
8626
            if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
8627
            {
8628
                if (gPageManager && gPageManager->getSetMarqueeText())
8629
                    gPageManager->getSetMarqueeText()(this);
8630
            }
8631
        }
8632
 
8633
        mChanged = false;
8634
    }
8635
}
8636
 
8637
void TButton::hide(bool total)
8638
{
8639
    DECL_TRACER("TButton::hide()");
8640
 
8641
//    if (type == MULTISTATE_GENERAL && ar == 1)
8642
//        mAniStop = true;
8643
 
8644
    if (!prg_stopped && total)
8645
    {
8646
        int rwidth = wt;
8647
        int rheight = ht;
8648
        int rleft = mPosLeft;
8649
        int rtop = mPosTop;
8650
 
8651
        ulong parent = mHandle & 0xffff0000;
8652
        THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
8653
 
8654
        if (tr && tr->mImageRefresh)
8655
        {
8656
            if (tr->mImageRefresh->isRunning())
8657
                tr->mImageRefresh->stop();
8658
        }
8659
#ifdef _SCALE_SKIA_
8660
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
8661
        {
8662
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
8663
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
8664
            rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
8665
            rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
8666
        }
8667
#endif
8668
        if (type == TEXT_INPUT)
8669
        {
8670
            if (gPageManager && gPageManager->getCallDropButton())
8671
                gPageManager->getCallDropButton()(mHandle);
8672
 
8673
            visible = false;
8674
            return;
8675
        }
8676
 
8677
        SkBitmap imgButton;
8678
 
8679
        if (rwidth < 0 || rheight < 0)
8680
        {
8681
            MSG_ERROR("Invalid size of image: " << rwidth << " x " << rheight);
8682
            return;
8683
        }
8684
 
8685
        try
8686
        {
8687
            if (!allocPixels(wt, ht, &imgButton))
8688
                return;
8689
 
8690
            imgButton.eraseColor(SK_ColorTRANSPARENT);
8691
        }
8692
        catch (std::exception& e)
8693
        {
8694
            MSG_ERROR("Error creating image: " << e.what());
8695
            visible = false;
8696
            return;
8697
        }
8698
 
8699
        if (!_displayButton && gPageManager)
8700
            _displayButton = gPageManager->getCallbackDB();
8701
 
8702
        if (_displayButton)
8703
        {
8704
            TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
8705
            _displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
8706
            mChanged = false;
8707
        }
8708
    }
8709
 
8710
    visible = false;
8711
}
8712
 
8713
bool TButton::isClickable(int x, int y)
8714
{
8715
    DECL_TRACER("TButton::isClickable()");
8716
 
8717
    if (mEnabled && /*((cp != 0 && ch != 0) || (lp != 0 && lv != 0) || !cm.empty() || !op.empty() || !pushFunc.empty() || isSystemButton()) &&*/ hs.compare("passThru") != 0)
8718
    {
8719
        if (x != -1 && y != -1 && hs.empty() && !mLastImage.empty() && isPixelTransparent(x, y))
8720
            return false;
8721
 
8722
        return true;
8723
    }
8724
 
8725
    return false;
8726
}
8727
 
8728
/**
8729
 * Handling of system button "connection state". It consists of 12 states
8730
 * indicating the network status. The states have the following meaning:
8731
 *
8732
 * 0      Diconnected (never was connected before since startup)
8733
 * 1 - 6  Connected (blink may be shown with dark and light green)
8734
 * 7, 8   Disconnected (timeout or loss of connection)
8735
 * 9 - 11 Connection in progress
8736
 */
8737
void TButton::funcNetwork(int state)
8738
{
8739
    DECL_TRACER("TButton::funcNetwork(int state)");
8740
 
8741
    TButtonStates *buttonStates = getButtonState();
8742
 
8743
    if (buttonStates)
8744
        buttonStates->setLastLevel(state);
8745
 
8746
    mActInstance = state;
8747
    mChanged = true;
8748
 
8749
    if (visible)
8750
        makeElement(state);
8751
}
8752
 
8753
/**
8754
 * Handling the timer event from the controller. This comes usualy every
8755
 * 20th part of a second (1 second / 20)
8756
 */
8757
void TButton::funcTimer(const amx::ANET_BLINK& blink)
8758
{
8759
    DECL_TRACER("TButton::funcTimer(const amx::ANET_BLINK& blink)");
8760
 
8761
    string tm;
8762
    std::stringstream sstr;
8763
 
8764
    switch (ad)
8765
    {
8766
        case 141:   // Standard time
8767
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
8768
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << ":"
8769
                 << std::setw(2) << std::setfill('0') << (int)blink.second;
8770
            mLastBlink = blink;
8771
        break;
8772
 
8773
        case 142:   // Time AM/PM
8774
        {
8775
            int hour = (blink.hour > 12) ? (blink.hour - 12) : blink.hour;
8776
            sstr << std::setw(2) << std::setfill('0') << hour << ":"
8777
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << " ";
8778
 
8779
            if (blink.hour <= 12)
8780
                sstr << "AM";
8781
            else
8782
                sstr << "PM";
8783
 
8784
            mLastBlink = blink;
8785
        }
8786
        break;
8787
 
8788
        case 143:   // Time 24 hours
8789
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
8790
                 << std::setw(2) << std::setfill('0') << (int)blink.minute;
8791
            mLastBlink = blink;
8792
        break;
8793
 
8794
        case 151:   // Weekday
8795
            switch (blink.weekday)
8796
            {
8797
                case 0: sstr << "Monday"; break;
8798
                case 1: sstr << "Tuesday"; break;
8799
                case 2: sstr << "Wednesday"; break;
8800
                case 3: sstr << "Thursday"; break;
8801
                case 4: sstr << "Friday"; break;
8802
                case 5: sstr << "Saturday"; break;
8803
                case 6: sstr << "Sunday"; break;
8804
            }
8805
        break;
8806
 
8807
        case 152:   // Date mm/dd
8808
            sstr << (int)blink.month << "/" << (int)blink.day;
8809
        break;
8810
 
8811
        case 153:   // Date dd/mm
8812
            sstr << (int)blink.day << "/" << (int)blink.month;
8813
        break;
8814
 
8815
        case 154:   // Date mm/dd/yyyy
8816
            sstr << (int)blink.month << "/" << (int)blink.day << "/" << (int)blink.year;
8817
        break;
8818
 
8819
        case 155:   // Date dd/mm/yyyy
8820
            sstr << blink.day << "/" << blink.month << "/" << blink.year;
8821
        break;
8822
 
8823
        case 156:   // Date month dd/yyyy
8824
            switch (blink.month)
8825
            {
8826
                case 1:  sstr << "January"; break;
8827
                case 2:  sstr << "February"; break;
8828
                case 3:  sstr << "March"; break;
8829
                case 4:  sstr << "April"; break;
8830
                case 5:  sstr << "May"; break;
8831
                case 6:  sstr << "June"; break;
8832
                case 7:  sstr << "July"; break;
8833
                case 8:  sstr << "August"; break;
8834
                case 9:  sstr << "September"; break;
8835
                case 10:  sstr << "October"; break;
8836
                case 11:  sstr << "November"; break;
8837
                case 12:  sstr << "December"; break;
8838
            }
8839
 
8840
            sstr << " " << (int)blink.day << "/" << (int)blink.year;
8841
        break;
8842
 
8843
        case 157:   // Date dd month yyyy
8844
            sstr << (int)blink.day;
8845
 
8846
            switch (blink.month)
8847
            {
8848
                case 1:  sstr << "January"; break;
8849
                case 2:  sstr << "February"; break;
8850
                case 3:  sstr << "March"; break;
8851
                case 4:  sstr << "April"; break;
8852
                case 5:  sstr << "May"; break;
8853
                case 6:  sstr << "June"; break;
8854
                case 7:  sstr << "July"; break;
8855
                case 8:  sstr << "August"; break;
8856
                case 9:  sstr << "September"; break;
8857
                case 10:  sstr << "October"; break;
8858
                case 11:  sstr << "November"; break;
8859
                case 12:  sstr << "December"; break;
8860
            }
8861
 
8862
            sstr << " " << (int)blink.year;
8863
        break;
8864
 
8865
        case 158:   // Date yyyy-mm-dd
8866
            sstr << (int)blink.year << "-" << (int)blink.month << "-" << (int)blink.day;
8867
        break;
8868
 
8869
        default:
8870
            return;
8871
    }
8872
 
8873
    vector<SR_T>::iterator iter;
8874
    tm = sstr.str();
8875
 
8876
    for (iter = sr.begin(); iter != sr.end(); ++iter)
8877
        iter->te = tm;
8878
 
8879
    mChanged = true;
8880
 
8881
    if (visible)
8882
        makeElement(mActInstance);
8883
}
8884
 
8885
bool TButton::isPixelTransparent(int x, int y)
8886
{
8887
    DECL_TRACER("TButton::isPixelTransparent(int x, int y)");
8888
 
8889
    // If there is no image we treat it as a non transpararent pixel.
8890
    if (sr[mActInstance].mi.empty() && sr[mActInstance].bm.empty())
8891
        return false;
8892
 
8893
    // The mLastImage must never be empty! Although this should never be true,
8894
    // we treat it as a transparent pixel if it happen.
8895
    if (mLastImage.empty())
8896
    {
8897
        MSG_ERROR("Internal error: No image for button available!");
8898
        return true;
8899
    }
8900
 
8901
    // Make sure the coordinates are inside the bounds. A test for a pixel
8902
    // outside the bounds would lead in an immediate exit because of an assert
8903
    // test in skia. Although this should not happen, we treat the pixel as
8904
    // transparent in this case, because the coordinates didn't hit the button.
8905
    if (x < 0 || x >= mLastImage.info().width() || y < 0 || y >= mLastImage.info().height())
8906
    {
8907
        MSG_ERROR("The X or Y coordinate is out of bounds!");
8908
        MSG_ERROR("X=" << x << ", Y=" << y << ", width=" << mLastImage.info().width() << ", height=" << mLastImage.info().height());
8909
        return true;
8910
    }
8911
 
8912
    float alpha = mLastImage.getAlphaf(x, y);   // Get the alpha value (0.0 to 1.0)
8913
 
8914
    if (alpha != 0.0)
8915
        return false;
8916
 
8917
    return true;
8918
}
8919
 
8920
bool TButton::checkForSound()
8921
{
8922
    DECL_TRACER("TButton::checkForSound()");
8923
 
8924
    vector<SR_T>::iterator iter;
8925
 
8926
    for (iter = sr.begin(); iter != sr.end(); ++iter)
8927
    {
8928
        if (!iter->sd.empty())
8929
            return true;
8930
    }
8931
 
8932
    return false;
8933
}
8934
 
8935
bool TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)
8936
{
8937
    DECL_TRACER("TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)");
8938
 
8939
    if (!bm)
8940
        return false;
8941
 
8942
    if (scaleWidth == 1.0 && scaleHeight == 1.0)
8943
        return true;
8944
 
8945
    SkPaint paint;
8946
    paint.setBlendMode(SkBlendMode::kSrc);
8947
//    paint.setFilterQuality(kHigh_SkFilterQuality);
8948
    // Calculate new dimension
8949
    SkImageInfo info = bm->info();
8950
    int width  = std::max(1, (int)((double)info.width() * scaleWidth));
8951
    int height = std::max(1, (int)((double)info.height() * scaleHeight));
8952
    MSG_DEBUG("Scaling image to size " << width << " x " << height);
8953
    // Create a canvas and draw new image
8954
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
8955
 
8956
    if (!allocPixels(width, height, bm))
8957
        return false;
8958
 
8959
    bm->eraseColor(SK_ColorTRANSPARENT);
8960
    SkCanvas can(*bm, SkSurfaceProps());
8961
    SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
8962
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
8963
    return true;
8964
}
8965
 
8966
bool TButton::stretchImageWidth(SkBitmap *bm, int width)
8967
{
8968
    DECL_TRACER("TButton::stretchImageWidth(SkBitmap *bm, int width)");
8969
 
8970
    if (!bm)
8971
        return false;
8972
 
8973
    int rwidth = width;
8974
    SkPaint paint;
8975
    paint.setBlendMode(SkBlendMode::kSrc);
8976
//    paint.setFilterQuality(kHigh_SkFilterQuality);
8977
 
8978
    SkImageInfo info = bm->info();
8979
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
8980
 
8981
    if (width <= 0)
8982
        rwidth = info.width() + width;
8983
 
8984
    if (rwidth <= 0)
8985
        rwidth = 1;
8986
 
8987
    MSG_DEBUG("Width: " << rwidth << ", Height: " << info.height());
8988
 
8989
    if (!allocPixels(rwidth, info.height(), bm))
8990
        return false;
8991
 
8992
    bm->eraseColor(SK_ColorTRANSPARENT);
8993
    SkCanvas can(*bm, SkSurfaceProps());
8994
    SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, info.height());
8995
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
8996
    return true;
8997
}
8998
 
8999
bool TButton::stretchImageHeight(SkBitmap *bm, int height)
9000
{
9001
    DECL_TRACER("TButton::stretchImageHeight(SkBitmap *bm, int height)");
9002
 
9003
    if (!bm)
9004
        return false;
9005
 
9006
    int rheight = height;
9007
    SkPaint paint;
9008
    paint.setBlendMode(SkBlendMode::kSrc);
9009
//    paint.setFilterQuality(kHigh_SkFilterQuality);
9010
 
9011
    SkImageInfo info = bm->info();
9012
 
9013
    if (height <= 0)
9014
        rheight = info.height() + height;
9015
 
9016
    if (rheight <= 0)
9017
        rheight = 1;
9018
 
9019
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
9020
    MSG_DEBUG("Width: " << info.width() << ", Height: " << rheight);
9021
 
9022
    if (!allocPixels(info.width(), rheight, bm))
9023
        return false;
9024
 
9025
    bm->eraseColor(SK_ColorTRANSPARENT);
9026
    SkCanvas can(*bm, SkSurfaceProps());
9027
    SkRect rect = SkRect::MakeXYWH(0, 0, info.width(), rheight);
9028
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
9029
    return true;
9030
}
9031
 
9032
bool TButton::stretchImageWH(SkBitmap *bm, int width, int height)
9033
{
9034
    DECL_TRACER("TButton::stretchImageWH(SkBitmap *bm, int width, int height)");
9035
 
9036
    if (!bm)
9037
        return false;
9038
 
9039
    int rwidth = width;
9040
    int rheight = height;
9041
    SkPaint paint;
9042
    paint.setBlendMode(SkBlendMode::kSrc);
9043
//    paint.setFilterQuality(kHigh_SkFilterQuality);
9044
 
9045
    SkImageInfo info = bm->info();
9046
 
9047
    if (width <= 0)
9048
        rwidth = info.width() + width;
9049
 
9050
    if (height <= 0)
9051
        rheight = info.height() + height;
9052
 
9053
    if (rheight <= 0)
9054
        rheight = 1;
9055
 
9056
    if (rwidth <= 0)
9057
        rwidth = 1;
9058
 
9059
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
9060
    MSG_DEBUG("Width: " << rwidth << ", Height: " << rheight);
9061
 
9062
    if (!allocPixels(rwidth, rheight, bm))
9063
        return false;
9064
 
9065
    bm->eraseColor(SK_ColorTRANSPARENT);
9066
    SkCanvas can(*bm, SkSurfaceProps());
9067
    SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, rheight);
9068
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
9069
    return true;
9070
}
9071
 
9072
/**
9073
 * This button got the click because it matches the coordinates of a mouse
9074
 * click. It checkes whether it is clickable or not. If it is clickable, it
9075
 * depends on the type of element what happens.
9076
 */
9077
bool TButton::doClick(int x, int y, bool pressed)
9078
{
9079
    DECL_TRACER("TButton::doClick(int x, int y, bool pressed)");
9080
 
9081
    if (!isClickable(x, y))
9082
        return false;
9083
 
9084
    amx::ANET_SEND scmd;
9085
    int instance = 0;
9086
    int sx = x, sy = y;
9087
    bool isSystem = isSystemButton();
9088
    int lastLevel = 0;
9089
    int lastJoyX = 0;
9090
    int lastJoyY = 0;
9091
    int lastSendLevelX = 0;
9092
    int lastSendLevelY = 0;
9093
    TButtonStates *buttonStates = getButtonState();
9094
 
9095
    if (buttonStates)
9096
    {
9097
        lastLevel = buttonStates->getLastLevel();
9098
        lastJoyX = buttonStates->getLastJoyX();
9099
        lastJoyY = buttonStates->getLastJoyY();
9100
        lastSendLevelX = buttonStates->getLastSendLevelX();
9101
        lastSendLevelY = buttonStates->getLastSendLevelY();
9102
    }
9103
    else
9104
    {
9105
        MSG_ERROR("Button states not found!");
9106
        return false;
9107
    }
9108
 
9109
    if (pressed && gPageManager && !checkForSound() && (ch > 0 || lv > 0 || !pushFunc.empty() || isSystem))
9110
    {
9111
        TSystemSound sysSound(TConfig::getSystemPath(TConfig::SOUNDS));
9112
 
9113
        if (gPageManager->havePlaySound() && sysSound.getSystemSoundState())
9114
            gPageManager->getCallPlaySound()(sysSound.getTouchFeedbackSound());
9115
    }
9116
 
9117
#ifdef _SCALE_SKIA_
9118
    // To be able to test a button for a transparent pixel, we must scale
9119
    // the coordinates because the test is made on the last drawn image and
9120
    // this image is scaled (if scaling is activated).
9121
    if (TConfig::getScale() && gPageManager && gPageManager->getScaleFactor() != 1.0)
9122
    {
9123
        double scaleFactor = gPageManager->getScaleFactor();
9124
        sx = (int)((double)x * scaleFactor);
9125
        sy = (int)((double)y * scaleFactor);
9126
    }
9127
#endif
9128
 
9129
    // Handle system buttons. Here the system keyboard buttons are handled.
9130
    if (_buttonPress && mActInstance >= 0 && static_cast<size_t>(mActInstance) < sr.size() && cp == 0 && ch > 0)
9131
    {
9132
        // Handling the keyboard buttons is very expensive. To not block too
9133
        // long, we let it run in a separate thread.
482 andreas 9134
        std::thread thr = std::thread([=] { _buttonPress(ch, static_cast<uint>(mHandle), pressed); });
446 andreas 9135
        thr.detach();
9136
    }
9137
 
9138
    // If the button is marked as password protected, then we must display
9139
    // a window with an input line to get the password from the user. Only if
9140
    // the password is equal to the password in the setup the button is
9141
    // processed further.
9142
    if (pressed && (pp > 0 || !mUser.empty()))
9143
    {
9144
        if (!mPassword.empty())
9145
        {
9146
            if (mPassword[0] == 1)  // No or invalid password?
9147
            {                       // Yes, then clear it and return
9148
                mPassword.clear();
9149
                return false;
9150
            }
9151
 
9152
            string pass;
9153
 
9154
            if (!mUser.empty())
9155
                pass = TConfig::getUserPassword(mUser);
9156
 
9157
            if (pass.empty() && pp > 0)
9158
            {
9159
                switch(pp)
9160
                {
9161
                    case 1: pass = TConfig::getPassword1(); break;
9162
                    case 2: pass = TConfig::getPassword2(); break;
9163
                    case 3: pass = TConfig::getPassword3(); break;
9164
                    case 4: pass = TConfig::getPassword4(); break;
9165
                    default:
9166
                        MSG_WARNING("Detected invalid password index " << pp);
9167
                        mPassword.clear();
9168
                        return false;
9169
                }
9170
            }
9171
 
9172
            if (pass != mPassword)  // Does the password not match?
9173
            {                       // Don't match then clear it and return
9174
                MSG_PROTOCOL("User typed wrong password!");
9175
                mPassword.clear();
9176
                return false;
9177
            }
9178
 
9179
            // The password match. We clear it and proceed.
9180
            mPassword.clear();
9181
        }
9182
        else if (gPageManager && gPageManager->getAskPassword())
9183
        {
9184
            string msg;
9185
 
9186
            if (mUser.empty())
9187
                msg = "Enter [" + intToString(pp) + "] password";
9188
            else
9189
                msg = "Enter password for user " + mUser;
9190
 
9191
            mPassword.clear();
9192
            gPageManager->getAskPassword()(mHandle, msg, "Password", x, y);
9193
            return true;
9194
        }
9195
        else
9196
            return false;
9197
    }
9198
 
9199
    if (type == GENERAL)
9200
    {
9201
        MSG_DEBUG("Button type: GENERAL; System button: " << (isSystem ? "YES" : "NO") << "; CH: " << cp << ":" << ch << "; AD: " << ap << ":" << ad);
9202
 
9203
        if (isSystem && ch == SYSTEM_ITEM_SOUNDSWITCH)   // Button sounds on/off
9204
        {
9205
            if (pressed)
9206
            {
9207
                MSG_TRACE("System button sounds are toggled ...");
9208
                TConfig::setTemporary(false);
9209
                bool sstate = TConfig::getSystemSoundState();
9210
 
9211
                if (sstate)
9212
                    mActInstance = instance = 0;
9213
                else
9214
                    mActInstance = instance = 1;
9215
 
9216
                TConfig::saveSystemSoundState(!sstate);
9217
                TConfig::saveSettings();
9218
                mChanged = true;
9219
                drawButton(mActInstance, true);
9220
            }
9221
        }
9222
        else if (isSystem && ch == SYSTEM_ITEM_SETUPPAGE)  // Enter setup page
9223
        {
9224
            if (pressed)
9225
            {
9226
                if (gPageManager && gPageManager->haveSetupPage())
9227
                    gPageManager->callSetupPage();
9228
            }
9229
        }
9230
        else if (isSystem && ch == SYSTEM_ITEM_SHUTDOWN)  // Shutdown program
9231
        {
9232
            if (pressed)
9233
            {
9234
                if (gPageManager && gPageManager->haveShutdown())
9235
                    gPageManager->callShutdown();
9236
            }
9237
        }
9238
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEUP)     // System volume up
9239
        {
9240
            TConfig::setTemporary(true);
9241
            int vol = TConfig::getSystemVolume() + 10;
9242
 
9243
            if (vol > 100)
9244
                vol = 100;
9245
 
9246
            if (pressed)
9247
                TConfig::saveSystemVolume(vol);
9248
 
9249
            if (pressed)
9250
                mActInstance = instance = 1;
9251
            else
9252
                mActInstance = instance = 0;
9253
 
9254
            mChanged = true;
9255
            drawButton(mActInstance, true);
9256
 
9257
            if (pressed && gPageManager)
9258
            {
9259
                int channel = TConfig::getChannel();
9260
                int system = TConfig::getSystem();
9261
 
9262
                amx::ANET_COMMAND cmd;
9263
                cmd.MC = 0x000a;
9264
                cmd.device1 = channel;
9265
                cmd.port1 = 0;
9266
                cmd.system = system;
9267
                cmd.data.message_value.system = system;
9268
                cmd.data.message_value.value = 9;   // System volume
9269
                cmd.data.message_value.content.integer = vol;
9270
                cmd.data.message_value.device = channel;
9271
                cmd.data.message_value.port = 0;    // Must be the address port of button
9272
                cmd.data.message_value.type = 0x20; // Unsigned int
9273
                gPageManager->doCommand(cmd);
9274
            }
9275
        }
9276
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEDOWN)     // System volume down
9277
        {
9278
            TConfig::setTemporary(true);
9279
            int vol = TConfig::getSystemVolume() - 10;
9280
 
9281
            if (vol < 0)
9282
                vol = 0;
9283
 
9284
            if (pressed)
9285
                TConfig::saveSystemVolume(vol);
9286
 
9287
            if (pressed)
9288
                mActInstance = instance = 1;
9289
            else
9290
                mActInstance = instance = 0;
9291
 
9292
            mChanged = true;
9293
            drawButton(mActInstance, true);
9294
 
9295
            if (pressed && gPageManager)
9296
            {
9297
                int channel = TConfig::getChannel();
9298
                int system = TConfig::getSystem();
9299
 
9300
                amx::ANET_COMMAND cmd;
9301
                cmd.MC = 0x000a;
9302
                cmd.device1 = channel;
9303
                cmd.port1 = 0;
9304
                cmd.system = system;
9305
                cmd.data.message_value.system = system;
9306
                cmd.data.message_value.value = 9;   // System volume
9307
                cmd.data.message_value.content.integer = vol;
9308
                cmd.data.message_value.device = channel;
9309
                cmd.data.message_value.port = 0;    // Must be the address port of button
9310
                cmd.data.message_value.type = 0x20; // Unsigned int
9311
                gPageManager->doCommand(cmd);
9312
            }
9313
        }
9314
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEMUTE)     // System mute
9315
        {
9316
            if (pressed)
9317
            {
9318
                TConfig::setTemporary(true);
9319
                bool mute = TConfig::getMuteState();
9320
 
9321
                if (mute)
9322
                    mActInstance = instance = 0;
9323
                else
9324
                    mActInstance = instance = 1;
9325
 
9326
                TConfig::setMuteState(!mute);
9327
 
9328
                if (gPageManager && gPageManager->getCallMuteSound())
9329
                    gPageManager->getCallMuteSound()(!mute);
9330
 
9331
                mChanged = true;
9332
                drawButton(mActInstance, true);
9333
            }
9334
        }
9335
        else if (isSystem && ch == SYSTEM_ITEM_BTSAVESETTINGS)     // System button OK: Save settings
9336
        {
9337
            if (pressed)
9338
            {
9339
                mActInstance = instance = 1;
9340
                TConfig::setTemporary(true);
9341
                TConfig::saveSettings();
9342
                drawButton(mActInstance, true);
9343
 
449 andreas 9344
                if (gPageManager && gPageManager->getDisplayMessage())
446 andreas 9345
                    gPageManager->getDisplayMessage()("Settings were saved!", "Info");
9346
                else
9347
                    MSG_INFO("Settings were saved.");
9348
            }
9349
            else
9350
            {
9351
                mActInstance = instance = 0;
9352
                drawButton(mActInstance, true);
9353
            }
9354
        }
9355
        else if (isSystem && ch == SYSTEM_ITEM_BTCANCELSETTINGS)     // System button Cancel: Cancel settings changes
9356
        {
9357
            if (pressed)
9358
            {
9359
                mActInstance = instance = 1;
9360
                TConfig::reset();
9361
                drawButton(mActInstance, true);
9362
            }
9363
            else
9364
            {
9365
                mActInstance = instance = 0;
9366
                drawButton(mActInstance, true);
9367
            }
9368
        }
9369
        else if (isSystem && ch == SYSTEM_ITEM_SIPENABLE)     // SIP: enabled/disabled
9370
        {
9371
            if (pressed)
9372
            {
9373
                TConfig::setTemporary(true);
9374
                bool st = TConfig::getSIPstatus();
9375
                mActInstance = instance = (st ? 0 : 1);
9376
                mChanged = true;
9377
                TConfig::setSIPstatus(!st);
9378
                drawButton(mActInstance, true);
9379
            }
9380
        }
9381
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGINFO)    // Debug info
9382
        {
9383
            if (pressed)
9384
            {
9385
                TConfig::setTemporary(true);
9386
                uint ll = TConfig::getLogLevelBits();
9387
                bool st = (ll & HLOG_INFO) ? true : false;
9388
                mActInstance = instance = (st ? 0 : 1);
9389
                ll = (st ? (ll &= RLOG_INFO) : (ll |= HLOG_INFO));
9390
                mChanged = true;
9391
                TConfig::saveLogLevel(ll);
9392
                drawButton(mActInstance, true);
9393
            }
9394
        }
9395
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGWARNING)    // Debug warning
9396
        {
9397
            if (pressed)
9398
            {
9399
                TConfig::setTemporary(true);
9400
                uint ll = TConfig::getLogLevelBits();
9401
                bool st = (ll & HLOG_WARNING) ? true : false;
9402
                mActInstance = instance = (st ? 0 : 1);
9403
                ll = (st ? (ll &= RLOG_WARNING) : (ll |= HLOG_WARNING));
9404
                mChanged = true;
9405
                TConfig::saveLogLevel(ll);
9406
                drawButton(mActInstance, true);
9407
            }
9408
        }
9409
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGERROR)    // Debug error
9410
        {
9411
            if (pressed)
9412
            {
9413
                TConfig::setTemporary(true);
9414
                uint ll = TConfig::getLogLevelBits();
9415
                bool st = (ll & HLOG_ERROR) ? true : false;
9416
                mActInstance = instance = (st ? 0 : 1);
9417
                ll = (st ? (ll &= RLOG_ERROR) : (ll |= HLOG_ERROR));
9418
                mChanged = true;
9419
                TConfig::saveLogLevel(ll);
9420
                drawButton(mActInstance, true);
9421
            }
9422
        }
9423
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGTRACE)    // Debug trace
9424
        {
9425
            if (pressed)
9426
            {
9427
                TConfig::setTemporary(true);
9428
                uint ll = TConfig::getLogLevelBits();
9429
                bool st = (ll & HLOG_TRACE) ? true : false;
9430
                mActInstance = instance = (st ? 0 : 1);
9431
                ll = (st ? (ll &= RLOG_TRACE) : (ll |= HLOG_TRACE));
9432
                mChanged = true;
9433
                TConfig::saveLogLevel(ll);
9434
                drawButton(mActInstance, true);
9435
            }
9436
        }
9437
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGDEBUG)    // Debug debug
9438
        {
9439
            if (pressed)
9440
            {
9441
                TConfig::setTemporary(true);
9442
                uint ll = TConfig::getLogLevelBits();
9443
                bool st = (ll & HLOG_DEBUG) ? true : false;
9444
                mActInstance = instance = (st ? 0 : 1);
9445
                ll = (st ? (ll &= RLOG_DEBUG) : (ll |= HLOG_DEBUG));
9446
                mChanged = true;
9447
                TConfig::saveLogLevel(ll);
9448
                drawButton(mActInstance, true);
9449
            }
9450
        }
9451
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROTOCOL)    // Debug protocol
9452
        {
9453
            if (pressed)
9454
            {
9455
                TConfig::setTemporary(true);
9456
                uint ll = TConfig::getLogLevelBits();
9457
                bool st = (ll & HLOG_PROTOCOL) == HLOG_PROTOCOL ? true : false;
9458
                mActInstance = instance = (st ? 0 : 1);
9459
                ll = (st ? (ll &= RLOG_PROTOCOL) : (ll |= HLOG_PROTOCOL));
9460
                mChanged = true;
9461
                TConfig::saveLogLevel(ll);
9462
                drawButton(mActInstance, true);
9463
 
9464
                if (gPageManager)
9465
                    gPageManager->updateActualPage();
9466
            }
9467
        }
9468
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGALL)    // Debug all
9469
        {
9470
            if (pressed)
9471
            {
9472
                TConfig::setTemporary(true);
9473
                uint ll = TConfig::getLogLevelBits();
9474
                bool st = (ll & HLOG_ALL) == HLOG_ALL ? true : false;
9475
                mActInstance = instance = (st ? 0 : 1);
9476
                ll = (st ? (ll &= RLOG_ALL) : (ll |= HLOG_ALL));
9477
                mChanged = true;
9478
                TConfig::saveLogLevel(ll);
9479
                drawButton(mActInstance, true);
9480
 
9481
                if (gPageManager)
9482
                    gPageManager->updateActualPage();
9483
            }
9484
        }
9485
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROFILE)    // Log profiling
9486
        {
9487
            if (pressed)
9488
            {
9489
                TConfig::setTemporary(true);
9490
                bool st = TConfig::getProfiling();
9491
                mActInstance = instance = (st ? 0 : 1);
9492
                mChanged = true;
9493
                TConfig::saveProfiling(!st);
9494
                drawButton(mActInstance, true);
9495
            }
9496
        }
9497
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGLONG)    // Log long format
9498
        {
9499
            if (pressed)
9500
            {
9501
                TConfig::setTemporary(true);
9502
                bool st = TConfig::isLongFormat();
9503
                mActInstance = instance = (st ? 0 : 1);
9504
                mChanged = true;
9505
                TConfig::saveFormat(!st);
9506
                drawButton(mActInstance, true);
9507
            }
9508
        }
9509
        else if (isSystem && ch == SYSTEM_ITEM_LOGRESET)    // Log reset path
9510
        {
9511
            if (pressed)
9512
            {
9513
                char *HOME = getenv("HOME");
9514
                string logFile = TConfig::getLogFile();
9515
 
9516
                if (HOME)
9517
                {
9518
                    logFile = HOME;
9519
                    logFile += "/tpanel/tpanel.log";
9520
                }
9521
 
9522
                ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
9523
                TConfig::setTemporary(true);
9524
                TConfig::saveLogFile(logFile);
9525
                MSG_DEBUG("Setting text \"" << logFile << "\" to button " << handleToString(handle));
9526
 
9527
                if (gPageManager)
9528
                    gPageManager->setTextToButton(handle, logFile, true);
9529
            }
9530
        }
9531
        else if (isSystem && ch == SYSTEM_ITEM_LOGFILEOPEN) // Log file dialog
9532
        {
9533
            if (pressed && gPageManager && gPageManager->getFileDialogFunction())
9534
            {
9535
                TConfig::setTemporary(true);
9536
                ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
9537
                string currFile = TConfig::getLogFile();
9538
                gPageManager->getFileDialogFunction()(handle, currFile, "*.log *.txt", "log");
9539
            }
9540
        }
9541
        else if (isSystem && ch == SYSTEM_ITEM_FTPDOWNLOAD)    // FTP download surface button
9542
        {
9543
            if (pressed)
9544
            {
9545
                TConfig::setTemporary(false);
9546
                string surfaceOld = TConfig::getFtpSurface();
9547
                TConfig::setTemporary(true);
9548
                string surfaceNew = TConfig::getFtpSurface();
9549
 
9550
                MSG_DEBUG("Surface difference: Old: " << surfaceOld << ", New: " << surfaceNew);
9551
 
9552
                if (gPageManager && gPageManager->getDownloadSurface())
9553
                {
9554
                    size_t size = gPageManager->getFtpSurfaceSize(surfaceNew);
9555
                    gPageManager->getDownloadSurface()(surfaceNew, size);
9556
                }
9557
            }
9558
        }
9559
        else if (isSystem && ch == SYSTEM_ITEM_FTPPASSIVE)    // FTP passive mode
9560
        {
9561
            if (pressed)
9562
            {
9563
                TConfig::setTemporary(true);
9564
                bool st = TConfig::getFtpPassive();
9565
                mActInstance = instance = (st ? 0 : 1);
9566
                mChanged = true;
9567
                TConfig::saveFtpPassive(!st);
9568
                drawButton(mActInstance, true);
9569
            }
9570
        }
9571
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYSYSSOUND)    // Play system sound
9572
        {
9573
            if (pressed)
9574
            {
9575
                TConfig::setTemporary(true);
9576
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSystemSound();
9577
 
9578
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
9579
                    gPageManager->getCallPlaySound()(sound);
9580
            }
9581
        }
9582
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYBEEP)    // Play single beep
9583
        {
9584
            if (pressed)
9585
            {
9586
                TConfig::setTemporary(true);
9587
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSingleBeepSound();
9588
 
9589
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
9590
                    gPageManager->getCallPlaySound()(sound);
9591
            }
9592
        }
9593
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYDBEEP)    // Play double beep
9594
        {
9595
            if (pressed)
9596
            {
9597
                TConfig::setTemporary(true);
9598
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getDoubleBeepSound();
9599
 
9600
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
9601
                    gPageManager->getCallPlaySound()(sound);
9602
            }
9603
        }
9604
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYTESTSOUND)    // Play test sound
9605
        {
9606
            if (pressed)
9607
            {
9608
                TConfig::setTemporary(true);
9609
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/audioTest.wav";
9610
 
9611
                if (gPageManager && gPageManager->getCallPlaySound())
9612
                    gPageManager->getCallPlaySound()(sound);
9613
            }
9614
        }
9615
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPV4)    // SIP: IPv4
9616
        {
9617
            if (pressed)
9618
            {
9619
                TConfig::setTemporary(true);
9620
                bool st = TConfig::getSIPnetworkIPv4();
9621
                mActInstance = instance = (st ? 0 : 1);
9622
                mChanged = true;
9623
                TConfig::setSIPnetworkIPv4(!st);
9624
                drawButton(mActInstance, true);
9625
            }
9626
        }
9627
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPV6)    // SIP: IPv6
9628
        {
9629
            if (pressed)
9630
            {
9631
                TConfig::setTemporary(true);
9632
                bool st = TConfig::getSIPnetworkIPv6();
9633
                mActInstance = instance = (st ? 0 : 1);
9634
                mChanged = true;
9635
                TConfig::setSIPnetworkIPv6(!st);
9636
                drawButton(mActInstance, true);
9637
            }
9638
        }
9639
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPHONE)    // SIP: internal phone
9640
        {
9641
            if (pressed)
9642
            {
9643
                TConfig::setTemporary(true);
9644
                bool st = TConfig::getSIPiphone();
9645
                mActInstance = instance = (st ? 0 : 1);
9646
                mChanged = true;
9647
                TConfig::setSIPiphone(!st);
9648
                drawButton(mActInstance, true);
9649
            }
9650
        }
9651
#ifdef __ANDROID__
9652
        else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT)    // Scale to fit
9653
        {
9654
            if (pressed)
9655
            {
9656
                TConfig::setTemporary(true);
9657
                bool st = TConfig::getScale();
9658
                mActInstance = instance = (st ? 0 : 1);
9659
                mChanged = true;
9660
                TConfig::saveScale(!st);
9661
                drawButton(mActInstance, true);
9662
            }
9663
        }
9664
        else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER)    // Show banner (disabled)
9665
        {
9666
            if (sr[0].oo < 0)
9667
            {
9668
                sr[0].oo = 128;
9669
                mChanged = true;
9670
                mActInstance = 0;
9671
                drawButton(mActInstance, true);
9672
            }
9673
        }
9674
#else
9675
        else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT)    // Scale to fit (disabled)
9676
        {
9677
            if (sr[0].oo < 0)
9678
            {
9679
                sr[0].oo = 128;
9680
                mChanged = true;
9681
                mActInstance = 0;
9682
                drawButton(mActInstance, true);
9683
            }
9684
        }
9685
        else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER)    // Show banner
9686
        {
9687
            if (pressed)
9688
            {
9689
                TConfig::setTemporary(true);
9690
                bool st = TConfig::showBanner();
9691
                mActInstance = instance = (st ? 0 : 1);
9692
                mChanged = true;
9693
                TConfig::saveBanner(st);
9694
                drawButton(mActInstance, true);
9695
            }
9696
        }
9697
#endif
9698
        else if (isSystem && ch == SYSTEM_ITEM_VIEWNOTOOLBAR)    // Suppress toolbar
9699
        {
9700
            if (pressed)
9701
            {
9702
                TConfig::setTemporary(true);
9703
                bool st = TConfig::getToolbarSuppress();
9704
                mActInstance = instance = (st ? 0 : 1);
9705
                mChanged = true;
9706
                TConfig::saveToolbarSuppress(!st);
9707
                drawButton(mActInstance, true);
9708
            }
9709
        }
9710
        else if (isSystem && ch == SYSTEM_ITEM_VIEWTOOLBAR)    // Force toolbar
9711
        {
9712
            if (pressed)
9713
            {
9714
                TConfig::setTemporary(true);
9715
 
9716
                if (TConfig::getToolbarSuppress())
9717
                {
9718
                    if (sr[0].oo < 0)
9719
                    {
9720
                        sr[0].oo = 128;
9721
                        mChanged = true;
9722
                        mActInstance = 0;
9723
                        drawButton(mActInstance, true);
9724
                    }
9725
                }
9726
                else
9727
                {
9728
                    if (sr[0].oo >= 0)
9729
                        sr[0].oo = -1;
9730
 
9731
                    bool st = TConfig::getToolbarForce();
9732
                    mActInstance = instance = (st ? 0 : 1);
9733
                    mChanged = true;
9734
                    TConfig::saveToolbarForce(!st);
9735
                    drawButton(mActInstance, true);
9736
                }
9737
            }
9738
        }
9739
        else if (isSystem && ch == SYSTEM_ITEM_VIEWROTATE)    // Lock rotation
9740
        {
9741
            if (pressed)
9742
            {
9743
                TConfig::setTemporary(true);
9744
                bool st = TConfig::getRotationFixed();
9745
                mActInstance = instance = (st ? 0 : 1);
9746
                mChanged = true;
9747
                TConfig::setRotationFixed(!st);
9748
                drawButton(mActInstance, true);
9749
            }
9750
        }
9751
        else if (fb == FB_MOMENTARY)
9752
        {
9753
            if (pressed)
9754
                instance = 1;
9755
            else
9756
                instance = 0;
9757
 
9758
            MSG_DEBUG("Flavor FB_MOMENTARY, instance=" << instance);
9759
            mActInstance = instance;
9760
            mChanged = true;
9761
 
9762
            if (pushFunc.empty() || (!pushFunc.empty() && instance == 0))
9763
                drawButton(instance);
9764
 
9765
            // If there is nothing in "hs", then it depends on the pixel of the
9766
            // layer. Only if the pixel the coordinates point to are not
9767
            // transparent, the button takes the click.
9768
            if (hs.empty() && isPixelTransparent(sx, sy))
9769
                return false;
9770
 
9771
            // Play sound, if one is defined
9772
            if (gPageManager)
9773
            {
9774
                if (pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
9775
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
9776
                else if (!pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
9777
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
9778
            }
9779
 
9780
            if (pushFunc.empty())   // Don't draw the button if it has a push function defined
9781
                showLastButton();
9782
            else
9783
                mActInstance = 0;
9784
        }
9785
        else if (fb == FB_CHANNEL || fb == FB_NONE)
9786
        {
9787
            if (pressed)
9788
                instance = 1;
9789
            else
9790
                instance = 0;
9791
 
9792
            MSG_DEBUG("Flavor FB_CHANNEL, instance=" << instance);
9793
            // If there is nothing in "hs", then it depends on the pixel of the
9794
            // layer. Only if the pixel the coordinates point to are not
9795
            // transparent, the button takes the click.
9796
            if (hs.empty() && isPixelTransparent(sx, sy))
9797
                return false;
9798
        }
9799
        else if (fb == FB_INV_CHANNEL)
9800
        {
9801
            if (pressed)
9802
                instance = 0;
9803
            else
9804
                instance = 1;
9805
 
9806
            MSG_DEBUG("Flavor FB_INV_CHANNEL, instance=" << instance);
9807
            // If there is nothing in "hs", then it depends on the pixel of the
9808
            // layer. Only if the pixel the coordinates point to are not
9809
            // transparent, the button takes the click.
9810
            if (hs.empty() && isPixelTransparent(sx, sy))
9811
                return false;
9812
 
9813
            // Play sound, if one is defined
9814
            if (gPageManager)
9815
            {
9816
                if (pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
9817
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
9818
                else if (!pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
9819
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
9820
            }
9821
        }
9822
        else if (fb == FB_ALWAYS_ON)
9823
        {
9824
            int oldInst = mActInstance;
9825
            instance = 1;
9826
            mActInstance = 1;
9827
            MSG_DEBUG("Flavor FB_ALWAYS_ON, instance=" << instance);
9828
 
9829
            if (oldInst != mActInstance)        // This should never become true!
9830
            {
9831
                mChanged = true;
9832
                drawButton(instance, false);
9833
            }
9834
 
9835
            // If there is nothing in "hs", then it depends on the pixel of the
9836
            // layer. Only if the pixel the coordinates point to are not
9837
            // transparent, the button takes the click.
9838
            if (hs.empty() && isPixelTransparent(sx, sy))
9839
                return false;
9840
 
9841
            // Play sound, if one is defined
9842
            if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
9843
                gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
9844
        }
9845
 
9846
        if ((cp && ch) || !op.empty())
9847
        {
9848
            scmd.device = TConfig::getChannel();
9849
            scmd.port = cp;
9850
            scmd.channel = ch;
9851
 
9852
            if (op.empty())
9853
            {
9854
                if (instance)
9855
                    scmd.MC = 0x0084;
9856
                else
9857
                    scmd.MC = 0x0085;
9858
            }
9859
            else
9860
            {
9861
                scmd.MC = 0x008b;
9862
                scmd.msg = op;
9863
            }
9864
 
9865
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << handleToString(mHandle));
9866
            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") << ")");
9867
 
9868
            if (gAmxNet)
9869
            {
9870
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
9871
                    gAmxNet->sendCommand(scmd);
9872
            }
9873
            else
9874
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
9875
        }
9876
        // If this button triggers a bargraph, we handle it here.
9877
        if (pressed && !vt.empty() && lp && lv && gPageManager)
9878
        {
9879
            TButton *bt = gPageManager->findBargraph(lp, lv, getParent());
9880
 
9881
            if (bt)
9882
            {
9883
                int level = bt->getLevelValue();
9884
 
9885
                if (vt == "rel")    // relative
9886
                {
9887
                    if (rv > 0)
9888
                    {
9889
                        mThreadRunMove = true;
9890
 
9891
                        level += va;
9892
                        int btRh = bt->getRangeHigh();
9893
                        int btRl = bt->getRangeLow();
9894
 
9895
                        if (level < btRl)
9896
                            level = btRl;
9897
                        else if (level > btRh)
9898
                            level = btRh;
9899
 
9900
                        for (int i = 0; i < rv; ++i)
9901
                        {
9902
                            if (!mThreadRunMove || level > btRh || level < btRl)
9903
                                break;
9904
 
9905
                            gPageManager->sendInternalLevel(lp, lv, level);
9906
 
9907
                            if (buttonStates && level != lastSendLevelX)
9908
                            {
9909
                                gPageManager->sendLevel(lp, lv, level);
9910
                                buttonStates->setLastSendLevelX(level);
9911
                                lastSendLevelX = level;
9912
                            }
9913
 
9914
                            level += va;
9915
                        }
9916
 
9917
                        mThreadRunMove = false;
9918
                    }
9919
                    else
9920
                    {
9921
                        level += va;
9922
 
9923
                        if (level < bt->getRangeLow())
9924
                            level = bt->getRangeLow();
9925
                        else if (level > bt->getRangeHigh())
9926
                            level = bt->getRangeHigh();
9927
 
9928
                        gPageManager->sendInternalLevel(lp, lv, level);
9929
 
9930
                        if (buttonStates && lastSendLevelX != (level))
9931
                        {
9932
                            gPageManager->sendLevel(lp, lv, level);
9933
                            buttonStates->setLastSendLevelX(level);
9934
                            lastSendLevelX = level;
9935
                        }
9936
                    }
9937
                }
9938
                else    // absolute
9939
                {
9940
                    gPageManager->sendInternalLevel(lp, lv, va);
9941
 
9942
                    if (buttonStates && lastSendLevelX != va)
9943
                    {
9944
                        gPageManager->sendLevel(lp, lv, va);
9945
                        buttonStates->setLastSendLevelX(va);
9946
                        lastSendLevelX = va;
9947
                    }
9948
                }
9949
            }
9950
            else
9951
                MSG_DEBUG("Found no bargraph with lp=" << lp << ", lv=" << lv);
9952
        }
9953
        else if (!pressed && !vt.empty() && lp && lv)
9954
        {
9955
            mThreadRunMove = false;
9956
        }
9957
    }
9958
    else if (type == MULTISTATE_GENERAL)
9959
    {
9960
        // Play sound, if one is defined
9961
        if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[mActInstance].sd.empty() && strCaseCompare(sr[mActInstance].sd, "None") != 0)
9962
            gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[mActInstance].sd);
9963
 
9964
        if ((cp && ch) || !op.empty())
9965
        {
9966
            scmd.device = TConfig::getChannel();
9967
            scmd.port = cp;
9968
            scmd.channel = ch;
9969
 
9970
            if (op.empty())
9971
            {
9972
                if (pressed || fb == FB_ALWAYS_ON)
9973
                    scmd.MC = 0x0084;
9974
                else
9975
                    scmd.MC = 0x0085;
9976
            }
9977
            else
9978
            {
9979
                scmd.MC = 0x008b;
9980
                scmd.msg = op;
9981
            }
9982
 
9983
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << handleToString(mHandle));
9984
            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") << ")");
9985
 
9986
            if (gAmxNet)
9987
            {
9988
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
9989
                    gAmxNet->sendCommand(scmd);
9990
            }
9991
            else
9992
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
9993
        }
9994
    }
9995
    else if (type == BARGRAPH && (lf == "active" || lf == "center"))
9996
    {
9997
        // Find the click position
9998
        int level = 0;
9999
 
10000
        if (!pressed)
10001
            mRunBargraphMove = false;
10002
 
10003
        if (!pressed && lf == "center")
10004
            level = (rh - rl) / 2;
10005
        else
10006
        {
10007
            if (dr.compare("horizontal") == 0)
10008
            {
10009
                level = x;
10010
                level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(wt) * static_cast<double>(level));
10011
            }
10012
            else
10013
            {
10014
                level = ht - y;
10015
                level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(ht) * static_cast<double>(level));
10016
            }
10017
        }
10018
 
10019
        if (isSystem)
10020
        {
10021
            // Draw the bargraph
10022
            if (!drawBargraph(mActInstance, level, visible))
10023
                return false;
10024
 
10025
            // Handle click
10026
            if (lv == 9)    // System volume button
10027
            {
10028
                if (!pressed)
10029
                {
10030
                    TConfig::saveSystemVolume(level);
10031
                    TConfig::saveSettings();
10032
                }
10033
            }
10034
        }
10035
        else if ((pressed && cp && ch) || (pressed && !op.empty()))
10036
        {
10037
            scmd.device = TConfig::getChannel();
10038
            scmd.port = cp;
10039
            scmd.channel = ch;
10040
 
10041
            if (op.empty())
10042
                scmd.MC = 0x0084;   // push button
10043
            else
10044
            {
10045
                scmd.MC = 0x008b;
10046
                scmd.msg = op;
10047
            }
10048
 
10049
            if (gAmxNet)
10050
                gAmxNet->sendCommand(scmd);
10051
            else
10052
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10053
        }
10054
 
10055
        if (!isSystem)
10056
        {
10057
            int distance = (lastLevel > level ? (lastLevel - level) : (level - lastLevel));
10058
            bool directionUp = (lastLevel > level);
10059
 
10060
            if (pressed && distance > 0)
10061
                runBargraphMove(distance, directionUp);
10062
            else if (!pressed)
10063
            {
10064
                if (lf == "active")
10065
                    level = lastLevel;
10066
                else if (level != lastLevel)
10067
                    drawBargraph(mActInstance, level);
10068
 
10069
                if (lp && lv && gPageManager && gPageManager->getLevelSendState())
10070
                {
10071
                    gPageManager->sendLevel(lp, lv, (ri ? ((rh - rl) - level) : level));
10072
                    lastSendLevelX = level;
10073
 
10074
                    if (buttonStates)
10075
                        buttonStates->setLastSendLevelX(level);
10076
                }
10077
            }
10078
 
10079
            if ((!pressed && cp && ch) || (!pressed && !op.empty()))
10080
            {
10081
                scmd.device = TConfig::getChannel();
10082
                scmd.port = cp;
10083
                scmd.channel = ch;
10084
 
10085
                if (op.empty())
10086
                    scmd.MC = 0x0085;   // release button
10087
                else
10088
                {
10089
                    scmd.MC = 0x008b;
10090
                    scmd.msg = op;
10091
                }
10092
 
10093
                if (gAmxNet)
10094
                    gAmxNet->sendCommand(scmd);
10095
                else
10096
                    MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10097
            }
10098
        }
10099
    }
10100
    else if (type == BARGRAPH && (lf == "drag" || lf == "dragCenter") && pressed)
10101
    {
10102
        mBarStartLevel = lastLevel;
10103
        int level;
10104
 
10105
        if (dr.compare("horizontal") == 0)
10106
        {
10107
            level = x;
10108
            level = (int)((double)(rh - rl) / (double)wt * (double)level);
10109
        }
10110
        else
10111
        {
10112
            level = ht - y;
10113
            level = (int)((double)(rh - rl) / (double)ht * (double)level);
10114
        }
10115
 
10116
        mBarThreshold = mBarStartLevel - level;
10117
        scmd.device = TConfig::getChannel();
10118
        scmd.port = cp;
10119
        scmd.channel = ch;
10120
 
10121
        if (op.empty())
10122
            scmd.MC = 0x0084;   // push button
10123
        else
10124
        {
10125
            scmd.MC = 0x008b;
10126
            scmd.msg = op;
10127
        }
10128
 
10129
        if (gAmxNet)
10130
            gAmxNet->sendCommand(scmd);
10131
        else
10132
            MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10133
    }
10134
    else if (type == BARGRAPH && (lf == "drag" || lf == "dragCenter") && !pressed)
10135
    {
10136
        if (lf == "dragCenter")
10137
        {
10138
            int level = (rh - rl) / 2;
10139
            mBarStartLevel = level;
10140
            // Draw the bargraph
10141
            if (!drawBargraph(mActInstance, level, visible))
10142
                return false;
10143
 
10144
            // Send the level
10145
            if (lp && lv && gPageManager && gPageManager->getLevelSendState())
10146
            {
10147
                scmd.device = TConfig::getChannel();
10148
                scmd.port = lp;
10149
                scmd.channel = lv;
10150
                scmd.level = lv;
10151
                scmd.value = (ri ? ((rh - rl) - level) : level);
10152
                scmd.MC = 0x008a;
10153
 
10154
                if (gAmxNet)
10155
                {
10156
                    if (lastSendLevelX != level)
10157
                        gAmxNet->sendCommand(scmd);
10158
 
10159
                    lastSendLevelX = level;
10160
 
10161
                    if (buttonStates)
10162
                        buttonStates->setLastSendLevelX(level);
10163
                }
10164
            }
10165
        }
10166
 
10167
        scmd.device = TConfig::getChannel();
10168
        scmd.port = cp;
10169
        scmd.channel = ch;
10170
 
10171
        if (op.empty())
10172
            scmd.MC = 0x0085;   // release button
10173
            else
10174
            {
10175
                scmd.MC = 0x008b;
10176
                scmd.msg = op;
10177
            }
10178
 
10179
            if (gAmxNet)
10180
                gAmxNet->sendCommand(scmd);
10181
        else
10182
            MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10183
    }
10184
    else if (type == TEXT_INPUT)
10185
    {
10186
        MSG_DEBUG("Text area detected. Switching on keyboard");
10187
        // Drawing background graphic (visible part of area)
10188
        drawTextArea(mActInstance);
10189
    }
10190
    else if (type == JOYSTICK && !lf.empty())
10191
    {
10192
        if (!pressed && (lf == "center" || lf == "dragCenter"))
10193
            sx = sy = (rh - rl) / 2;
10194
 
10195
        if (pressed && ((cp && ch) || !op.empty()))
10196
        {
10197
            scmd.device = TConfig::getChannel();
10198
            scmd.port = cp;
10199
            scmd.channel = ch;
10200
 
10201
            if (op.empty())
10202
                scmd.MC = 0x0084;
10203
            else
10204
            {
10205
                scmd.MC = 0x008b;
10206
                scmd.msg = op;
10207
            }
10208
 
10209
            if (gAmxNet)
10210
                gAmxNet->sendCommand(scmd);
10211
            else
10212
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10213
        }
10214
 
10215
        if (!drawJoystick(sx, sy))
10216
            return false;
10217
 
10218
        // Send the levels
10219
        if (lp && lv && gPageManager && gPageManager->getLevelSendState())
10220
        {
10221
            scmd.device = TConfig::getChannel();
10222
            scmd.port = lp;
10223
            scmd.channel = lv;
10224
            scmd.level = lv;
10225
            scmd.value = (ri ? ((rh - rl) - sx) : sx);
10226
            scmd.MC = 0x008a;
10227
 
10228
            if (gAmxNet)
10229
            {
10230
                if (lastSendLevelX != scmd.value)
10231
                    gAmxNet->sendCommand(scmd);
10232
 
10233
                lastJoyX = sx;
10234
                lastSendLevelX = scmd.value;
10235
 
10236
                if (buttonStates)
10237
                    buttonStates->setLastSendLevelX(lastSendLevelX);
10238
            }
10239
 
10240
            scmd.channel = lv + 1;
10241
            scmd.level = lv + 1;
10242
            scmd.value = (ji ? ((rh - rl) - sy) : sy);
10243
 
10244
            if (gAmxNet)
10245
            {
10246
                if (lastSendLevelY != scmd.value)
10247
                    gAmxNet->sendCommand(scmd);
10248
 
10249
                lastJoyY = sy;
10250
                lastSendLevelY = scmd.value;
10251
 
10252
                if (buttonStates)
10253
                    buttonStates->setLastSendLevelY(lastSendLevelY);
10254
            }
10255
        }
10256
 
10257
        if (!pressed && ((cp && ch) || !op.empty()))
10258
        {
10259
            scmd.device = TConfig::getChannel();
10260
            scmd.port = cp;
10261
            scmd.channel = ch;
10262
 
10263
            if (op.empty())
10264
                scmd.MC = 0x0085;
10265
            else
10266
            {
10267
                scmd.MC = 0x008b;
10268
                scmd.msg = op;
10269
            }
10270
 
10271
            if (gAmxNet)
10272
                gAmxNet->sendCommand(scmd);
10273
            else
10274
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10275
        }
10276
    }
10277
    else if (type == JOYSTICK && lf.empty())
10278
    {
10279
        if ((cp && ch) || !op.empty())
10280
        {
10281
            scmd.device = TConfig::getChannel();
10282
            scmd.port = cp;
10283
            scmd.channel = ch;
10284
            scmd.MC = 0;
10285
 
10286
            if (op.empty())
10287
                scmd.MC = (pressed ? 0x0084 : 0x0085);
10288
            else if (pressed)
10289
            {
10290
                scmd.MC = 0x008b;
10291
                scmd.msg = op;
10292
            }
10293
 
10294
            if (gAmxNet && scmd.MC != 0)
10295
                gAmxNet->sendCommand(scmd);
10296
            else if (!gAmxNet)
10297
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
10298
        }
10299
    }
10300
 
10301
    /* FIXME: Move the following to class TPageManager!
10302
     *        To do that, the preconditions are to be implemented. It must be
10303
     *        possible to find the button and get access to the credentials
10304
     *        of it.
10305
     */
10306
    if (!pushFunc.empty() && pressed)
10307
    {
10308
        MSG_DEBUG("Executing a push function ...");
10309
        vector<PUSH_FUNC_T>::iterator iter;
10310
 
10311
        for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
10312
        {
10313
            MSG_DEBUG("Testing for function \"" << iter->pfType << "\"");
10314
 
10315
            if (fb == FB_MOMENTARY || fb == FB_NONE)
10316
                mActInstance = 0;
10317
            else if (fb == FB_ALWAYS_ON || fb == FB_INV_CHANNEL)
10318
                mActInstance = 1;
10319
 
10320
            if (strCaseCompare(iter->pfType, "SSHOW") == 0)            // show popup
10321
            {
10322
                if (gPageManager)
10323
                    gPageManager->showSubPage(iter->pfName);
10324
            }
10325
            else if (strCaseCompare(iter->pfType, "SHIDE") == 0)       // hide popup
10326
            {
10327
                if (gPageManager)
10328
                    gPageManager->hideSubPage(iter->pfName);
10329
            }
10330
            else if (strCaseCompare(iter->pfType, "SCGROUP") == 0)     // hide group
10331
            {
10332
                if (gPageManager)
10333
                    gPageManager->closeGroup(iter->pfName);
10334
            }
10335
            else if (strCaseCompare(iter->pfType, "SCPAGE") == 0)      // flip to page
10336
            {
10337
                if (gPageManager && !iter->pfName.empty())
10338
                    gPageManager->setPage(iter->pfName);
10339
            }
10340
            else if (strCaseCompare(iter->pfType, "STAN") == 0)        // Flip to standard page
10341
            {
10342
                if (gPageManager)
10343
                {
10344
                    if (!iter->pfName.empty())
10345
                        gPageManager->setPage(iter->pfName);
10346
                    else
10347
                    {
10348
                        TPage *page = gPageManager->getActualPage();
10349
 
10350
                        if (!page)
10351
                        {
10352
                            MSG_DEBUG("Internal error: No actual page found!");
10353
                            return false;
10354
                        }
10355
 
10356
                        TSettings *settings = gPageManager->getSettings();
10357
 
10358
                        if (settings && settings->getPowerUpPage().compare(page->getName()) != 0)
10359
                            gPageManager->setPage(settings->getPowerUpPage());
10360
                    }
10361
                }
10362
            }
10363
            else if (strCaseCompare(iter->pfType, "FORGET") == 0)      // Flip to page and forget
10364
            {
10365
                if (gPageManager && !iter->pfName.empty())
10366
                        gPageManager->setPage(iter->pfName, true);
10367
            }
10368
            else if (strCaseCompare(iter->pfType, "PREV") == 0)        // Flip to previous page
10369
            {
10370
                if (gPageManager)
10371
                {
10372
                    int old = gPageManager->getPreviousPageNumber();
10373
 
10374
                    if (old > 0)
10375
                        gPageManager->setPage(old);
10376
                }
10377
            }
10378
            else if (strCaseCompare(iter->pfType, "STOGGLE") == 0)     // Toggle popup state
10379
            {
10380
                if (!iter->pfName.empty() && gPageManager)
10381
                {
10382
                    TSubPage *page = gPageManager->getSubPage(iter->pfName);
10383
 
10384
                    if (!page)      // Is the page not in cache?
10385
                    {               // No, then load it
10386
                        gPageManager->showSubPage(iter->pfName);
10387
                        return true;
10388
                    }
10389
 
10390
                    if (page->isVisible())
10391
                        gPageManager->hideSubPage(iter->pfName);
10392
                    else
10393
                        gPageManager->showSubPage(iter->pfName);
10394
                }
10395
            }
10396
            else if (strCaseCompare(iter->pfType, "SCPANEL") == 0)   // Hide all popups
10397
            {
10398
                if (gPageManager)
10399
                {
10400
                    TSubPage *page = gPageManager->getFirstSubPage();
10401
 
10402
                    while (page)
10403
                    {
10404
                        page->drop();
10405
                        page = gPageManager->getNextSubPage();
10406
                    }
10407
                }
10408
            }
10409
            else
10410
            {
10411
                MSG_WARNING("Unknown page flip command " << iter->pfType);
10412
            }
10413
        }
10414
    }
10415
 
10416
    if (!cm.empty() && co == 0 && pressed)      // Feed command to ourself?
10417
    {                                           // Yes, then feed it into command queue.
10418
        MSG_DEBUG("Button has a self feed command");
10419
 
10420
        int channel = TConfig::getChannel();
10421
        int system = TConfig::getSystem();
10422
 
10423
        if (gPageManager)
10424
        {
10425
            amx::ANET_COMMAND cmd;
10426
            cmd.intern = true;
10427
            cmd.MC = 0x000c;
10428
            cmd.device1 = channel;
10429
            cmd.port1 = 1;
10430
            cmd.system = system;
10431
            cmd.data.message_string.device = channel;
10432
            cmd.data.message_string.port = 1;   // Must be 1
10433
            cmd.data.message_string.system = system;
10434
            cmd.data.message_string.type = 1;   // 8 bit char string
10435
 
10436
            vector<string>::iterator iter;
10437
 
10438
            for (iter = cm.begin(); iter != cm.end(); ++iter)
10439
            {
10440
                cmd.data.message_string.length = iter->length();
10441
                memset(&cmd.data.message_string.content, 0, sizeof(cmd.data.message_string.content));
10442
                strncpy((char *)&cmd.data.message_string.content, iter->c_str(), sizeof(cmd.data.message_string.content)-1);
10443
                MSG_DEBUG("Executing system command: " << *iter);
10444
                gPageManager->doCommand(cmd);
10445
            }
10446
        }
10447
    }
10448
    else if (!cm.empty() && pressed)
10449
    {
10450
        MSG_DEBUG("Button sends a command on port " << co);
10451
 
10452
        if (gPageManager)
10453
        {
10454
            vector<string>::iterator iter;
10455
 
10456
            for (iter = cm.begin(); iter != cm.end(); ++iter)
10457
                gPageManager->sendCommandString(co, *iter);
10458
        }
10459
    }
10460
 
10461
    return true;
10462
}
10463
 
10464
/**
10465
 * Based on the pixels in the \a basePix, the function decides whether to return
10466
 * the value of \a col1 or \a col2. A red pixel returns the color \a col1 and
10467
 * a green pixel returns the color \a col2. If there is no red and no green
10468
 * pixel, a transparent pixel is returned.
10469
 *
10470
 * @param basePix
10471
 * This is a pixel from a mask containing red and/or green pixels.
10472
 *
10473
 * @param maskPix
10474
 * This is a pixel from a mask containing more or less tranparent pixels. If
10475
 * the alpha channel of \a basePix is 0 (transparent) this pixel is returned.
10476
 *
10477
 * @param col1
10478
 * The first color.
10479
 *
10480
 * @param col2
10481
 * The second color.
10482
 *
10483
 * @return
10484
 * An array containing the color for one pixel.
10485
 */
10486
SkColor TButton::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
10487
{
10488
    uint alpha = SkColorGetA(basePix);
10489
    uint green = SkColorGetG(basePix);
10490
    uint red = 0;
10491
 
10492
    if (isBigEndian())
10493
        red = SkColorGetB(basePix);
10494
    else
10495
        red = SkColorGetR(basePix);
10496
 
10497
    if (alpha == 0)
10498
        return maskPix;
10499
 
10500
    if (red && green)
10501
    {
10502
        if (red < green)
10503
            return col2;
10504
 
10505
        return col1;
10506
    }
10507
 
10508
    if (red)
10509
        return col1;
10510
 
10511
    if (green)
10512
        return col2;
10513
 
10514
    return SK_ColorTRANSPARENT; // transparent pixel
10515
}
10516
 
10517
TEXT_EFFECT TButton::textEffect(const std::string& effect)
10518
{
10519
    DECL_TRACER("TButton::textEffect(const std::string& effect)");
10520
 
10521
    if (effect == "Outline-S")
10522
        return EFFECT_OUTLINE_S;
10523
    else if (effect == "Outline-M")
10524
        return EFFECT_OUTLINE_M;
10525
    else if (effect == "Outline-L")
10526
        return EFFECT_OUTLINE_L;
10527
    else if (effect == "Outline-X")
10528
        return EFFECT_OUTLINE_X;
10529
    else if (effect == "Glow-S")
10530
        return EFFECT_GLOW_S;
10531
    else if (effect == "Glow-M")
10532
        return EFFECT_GLOW_M;
10533
    else if (effect == "Glow-L")
10534
        return EFFECT_GLOW_L;
10535
    else if (effect == "Glow-X")
10536
        return EFFECT_GLOW_X;
10537
    else if (effect == "Soft Drop Shadow 1")
10538
        return EFFECT_SOFT_DROP_SHADOW_1;
10539
    else if (effect == "Soft Drop Shadow 2")
10540
        return EFFECT_SOFT_DROP_SHADOW_2;
10541
    else if (effect == "Soft Drop Shadow 3")
10542
        return EFFECT_SOFT_DROP_SHADOW_3;
10543
    else if (effect == "Soft Drop Shadow 4")
10544
        return EFFECT_SOFT_DROP_SHADOW_4;
10545
    else if (effect == "Soft Drop Shadow 5")
10546
        return EFFECT_SOFT_DROP_SHADOW_5;
10547
    else if (effect == "Soft Drop Shadow 6")
10548
        return EFFECT_SOFT_DROP_SHADOW_6;
10549
    else if (effect == "Soft Drop Shadow 7")
10550
        return EFFECT_SOFT_DROP_SHADOW_7;
10551
    else if (effect == "Soft Drop Shadow 8")
10552
        return EFFECT_SOFT_DROP_SHADOW_8;
10553
    else if (effect == "Medium Drop Shadow 1")
10554
        return EFFECT_MEDIUM_DROP_SHADOW_1;
10555
    else if (effect == "Medium Drop Shadow 2")
10556
        return EFFECT_MEDIUM_DROP_SHADOW_2;
10557
    else if (effect == "Medium Drop Shadow 3")
10558
        return EFFECT_MEDIUM_DROP_SHADOW_3;
10559
    else if (effect == "Medium Drop Shadow 4")
10560
        return EFFECT_MEDIUM_DROP_SHADOW_4;
10561
    else if (effect == "Medium Drop Shadow 5")
10562
        return EFFECT_MEDIUM_DROP_SHADOW_5;
10563
    else if (effect == "Medium Drop Shadow 6")
10564
        return EFFECT_MEDIUM_DROP_SHADOW_6;
10565
    else if (effect == "Medium Drop Shadow 7")
10566
        return EFFECT_MEDIUM_DROP_SHADOW_7;
10567
    else if (effect == "Medium Drop Shadow 8")
10568
        return EFFECT_MEDIUM_DROP_SHADOW_8;
10569
    else if (effect == "Hard Drop Shadow 1")
10570
        return EFFECT_HARD_DROP_SHADOW_1;
10571
    else if (effect == "Hard Drop Shadow 2")
10572
        return EFFECT_HARD_DROP_SHADOW_2;
10573
    else if (effect == "Hard Drop Shadow 3")
10574
        return EFFECT_HARD_DROP_SHADOW_3;
10575
    else if (effect == "Hard Drop Shadow 4")
10576
        return EFFECT_HARD_DROP_SHADOW_4;
10577
    else if (effect == "Hard Drop Shadow 5")
10578
        return EFFECT_HARD_DROP_SHADOW_5;
10579
    else if (effect == "Hard Drop Shadow 6")
10580
        return EFFECT_HARD_DROP_SHADOW_6;
10581
    else if (effect == "Hard Drop Shadow 7")
10582
        return EFFECT_HARD_DROP_SHADOW_7;
10583
    else if (effect == "Hard Drop Shadow 8")
10584
        return EFFECT_HARD_DROP_SHADOW_8;
10585
    else if (effect == "Soft Drop Shadow 1 with outline")
10586
        return EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE;
10587
    else if (effect == "Soft Drop Shadow 2 with outline")
10588
        return EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE;
10589
    else if (effect == "Soft Drop Shadow 3 with outline")
10590
        return EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE;
10591
    else if (effect == "Soft Drop Shadow 4 with outline")
10592
        return EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE;
10593
    else if (effect == "Soft Drop Shadow 5 with outline")
10594
        return EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE;
10595
    else if (effect == "Soft Drop Shadow 6 with outline")
10596
        return EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE;
10597
    else if (effect == "Soft Drop Shadow 7 with outline")
10598
        return EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE;
10599
    else if (effect == "Soft Drop Shadow 8 with outline")
10600
        return EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE;
10601
    else if (effect == "Medium Drop Shadow 1 with outline")
10602
        return EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE;
10603
    else if (effect == "Medium Drop Shadow 2 with outline")
10604
        return EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE;
10605
    else if (effect == "Medium Drop Shadow 3 with outline")
10606
        return EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE;
10607
    else if (effect == "Medium Drop Shadow 4 with outline")
10608
        return EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE;
10609
    else if (effect == "Medium Drop Shadow 5 with outline")
10610
        return EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE;
10611
    else if (effect == "Medium Drop Shadow 6 with outline")
10612
        return EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE;
10613
    else if (effect == "Medium Drop Shadow 7 with outline")
10614
        return EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE;
10615
    else if (effect == "Medium Drop Shadow 8 with outline")
10616
        return EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE;
10617
    else if (effect == "Hard Drop Shadow 1 with outline")
10618
        return EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE;
10619
    else if (effect == "Hard Drop Shadow 2 with outline")
10620
        return EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE;
10621
    else if (effect == "Hard Drop Shadow 3 with outline")
10622
        return EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE;
10623
    else if (effect == "Hard Drop Shadow 4 with outline")
10624
        return EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE;
10625
    else if (effect == "Hard Drop Shadow 5 with outline")
10626
        return EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE;
10627
    else if (effect == "Hard Drop Shadow 6 with outline")
10628
        return EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE;
10629
    else if (effect == "Hard Drop Shadow 7 with outline")
10630
        return EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE;
10631
    else if (effect == "Hard Drop Shadow 8 with outline")
10632
        return EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE;
10633
 
10634
    return EFFECT_NONE;
10635
}
10636
 
10637
bool TButton::isSystemButton()
10638
{
10639
    DECL_TRACER("TButton::isSystemButton()");
10640
 
10641
    if (type == MULTISTATE_BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
10642
        return true;
10643
    else if (type == BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
10644
        return true;
10645
    else if (type == LISTBOX && ap == 0 && ad > 0 && ti >= SYSTEM_PAGE_START)
10646
        return true;
10647
    else if (ap == 0 && TSystem::isSystemButton(ad))
10648
        return true;
10649
    else if (cp == 0 && TSystem::isSystemButton(ch))
10650
        return true;
10651
 
10652
    return false;
10653
}
10654
 
10655
THR_REFRESH_t *TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)
10656
{
10657
    DECL_TRACER("TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)");
10658
 
10659
    THR_REFRESH_t *p = mThrRefresh;
10660
    THR_REFRESH_t *r, *last = p;
10661
 
10662
    if (!refr || !handle || !parent || bi <= 0)
10663
    {
10664
        MSG_ERROR("Invalid parameter!");
10665
        return nullptr;
10666
    }
10667
 
10668
    r = new THR_REFRESH_t;
10669
    r->mImageRefresh = refr;
10670
    r->handle = handle;
10671
    r->parent = parent;
10672
    r->bi = bi;
10673
    r->next = nullptr;
10674
 
10675
    // If the chain is empty, add the new item;
10676
    if (!mThrRefresh)
10677
        mThrRefresh = r;
10678
    else    // Find the end and append the item
10679
    {
10680
        while (p)
10681
        {
10682
            last = p;
10683
 
10684
            if (p->handle == handle && p->parent == parent && p->bi == bi)
10685
            {
10686
                MSG_WARNING("Duplicate button found! Didn't add it again.");
10687
                delete r;
10688
                return p;
10689
            }
10690
 
10691
            p = p->next;
10692
        }
10693
 
10694
        last->next = r;
10695
    }
10696
 
10697
    MSG_DEBUG("New dynamic button added.");
10698
    return r;
10699
}
10700
 
10701
THR_REFRESH_t *TButton::_findResource(ulong handle, ulong parent, int bi)
10702
{
10703
    DECL_TRACER("TButton::_findResource(ulong handle, ulong parent, int bi)");
10704
 
10705
    THR_REFRESH_t *p = mThrRefresh;
10706
 
10707
    while (p)
10708
    {
10709
        if (p->handle == handle && p->parent == parent && p->bi == bi)
10710
            return p;
10711
 
10712
        p = p->next;
10713
    }
10714
 
10715
    return nullptr;
10716
}
10717
 
10718
void TButton::addToBitmapCache(BITMAP_CACHE& bc)
10719
{
10720
    DECL_TRACER("TButton::addToBitmapCache(BITMAP_CACHE& bc)");
10721
 
10722
    if (nBitmapCache.size() == 0)
10723
    {
10724
        nBitmapCache.push_back(bc);
10725
        return;
10726
    }
10727
 
10728
    vector<BITMAP_CACHE>::iterator iter;
10729
 
10730
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10731
    {
10732
        if (iter->handle == bc.handle && iter->parent == bc.parent && iter->bi == bc.bi)
10733
        {
10734
            nBitmapCache.erase(iter);
10735
            nBitmapCache.push_back(bc);
10736
            return;
10737
        }
10738
    }
10739
 
10740
    nBitmapCache.push_back(bc);
10741
}
10742
 
10743
BITMAP_CACHE& TButton::getBCentryByHandle(ulong handle, ulong parent)
10744
{
10745
    DECL_TRACER("TButton::getBCentryByHandle(ulong handle, ulong parent)");
10746
 
10747
    if (nBitmapCache.size() == 0)
10748
        return mBCDummy;
10749
 
10750
    vector<BITMAP_CACHE>::iterator iter;
10751
 
10752
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10753
    {
10754
        if (iter->handle == handle && iter->parent == parent)
10755
            return *iter;
10756
    }
10757
 
10758
    return mBCDummy;
10759
}
10760
 
10761
BITMAP_CACHE& TButton::getBCentryByBI(int bIdx)
10762
{
10763
    DECL_TRACER("TButton::getBCentryByBI(int bIdx)");
10764
 
10765
    if (nBitmapCache.size() == 0)
10766
        return mBCDummy;
10767
 
10768
    vector<BITMAP_CACHE>::iterator iter;
10769
 
10770
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10771
    {
10772
        if (iter->bi == bIdx)
10773
            return *iter;
10774
    }
10775
 
10776
        return mBCDummy;
10777
}
10778
 
10779
void TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)
10780
{
10781
    DECL_TRACER("TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)");
10782
 
10783
    if (nBitmapCache.size() == 0 || !elem)
10784
        return;
10785
 
10786
    vector<BITMAP_CACHE>::iterator iter;
10787
 
10788
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10789
    {
10790
        if (iter == *elem)
10791
        {
10792
            nBitmapCache.erase(iter);
10793
            return;
10794
        }
10795
    }
10796
}
10797
 
10798
void TButton::setReady(ulong handle)
10799
{
10800
    DECL_TRACER("TButton::setReady(ulong handle)");
10801
 
10802
    if (nBitmapCache.size() == 0)
10803
        return;
10804
 
10805
    vector<BITMAP_CACHE>::iterator iter;
10806
 
10807
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10808
    {
10809
        if (iter->handle == handle)
10810
        {
10811
            iter->ready = true;
10812
            return;
10813
        }
10814
    }
10815
}
10816
 
10817
void TButton::setInvalid(ulong handle)
10818
{
10819
    DECL_TRACER("TButton::setInvalid(ulong handle)");
10820
 
10821
    if (nBitmapCache.size() == 0)
10822
        return;
10823
 
10824
    vector<BITMAP_CACHE>::iterator iter;
10825
 
10826
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10827
    {
10828
        if (iter->handle == handle)
10829
        {
10830
            nBitmapCache.erase(iter);
10831
            return;
10832
        }
10833
    }
10834
}
10835
 
10836
void TButton::setBCBitmap(ulong handle, SkBitmap& bm)
10837
{
10838
    DECL_TRACER("TButton::setBCBitmap(ulong handle, SkBitmap& bm)");
10839
 
10840
    if (nBitmapCache.size() == 0)
10841
        return;
10842
 
10843
    vector<BITMAP_CACHE>::iterator iter;
10844
 
10845
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10846
    {
10847
        if (iter->handle == handle)
10848
        {
10849
            iter->bitmap = bm;
10850
            return;
10851
        }
10852
    }
10853
}
10854
 
10855
void TButton::showBitmapCache()
10856
{
10857
    DECL_TRACER("TButton::showBitmapCache()");
10858
 
10859
    vector<BITMAP_CACHE>::iterator iter;
10860
    bool found;
10861
 
10862
    while (nBitmapCache.size() > 0)
10863
    {
10864
        found = false;
10865
 
10866
        for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
10867
        {
10868
            if (iter->ready)
10869
            {
10870
                if (_displayButton)
10871
                {
10872
                    TBitmap image((unsigned char *)iter->bitmap.getPixels(), iter->bitmap.info().width(), iter->bitmap.info().height());
10873
                    _displayButton(iter->handle, iter->parent, image, iter->width, iter->height, iter->left, iter->top, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
10874
 
10875
                    if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
10876
                    {
10877
                        if (gPageManager && gPageManager->getSetMarqueeText())
10878
                            gPageManager->getSetMarqueeText()(this);
10879
                    }
10880
 
10881
                    mChanged = false;
10882
                }
10883
 
10884
                nBitmapCache.erase(iter);
10885
                found = true;
10886
                break;
10887
            }
10888
        }
10889
 
10890
        if (!found)
10891
            break;
10892
    }
10893
}
10894
 
10895
uint32_t TButton::pixelMix(uint32_t s, uint32_t d, uint32_t a, PMIX mix)
10896
{
10897
    DECL_TRACER("TButton::pixelMultiply(uint32_t s, uint32_t d)");
10898
 
10899
    uint32_t r = 0;
10900
 
10901
    switch(mix)
10902
    {
10903
        case PMIX_SRC:          r = s; break;                                                   // SRC
10904
        case PMIX_DST:          r = d; break;                                                   // DST
10905
        case PMIX_MULTIPLY:     r = s * (255 - (d * a)) + d * (255 - (s * a)) + s * d; break;   // Multiply
10906
        case PMIX_PLUS:         r = std::min(s + d, (uint32_t)255); break;                      // plus
10907
        case PMIX_XOR:          r = s * (255 - (d * a)) + d * (255 - (s * a)); break;           // XOr
10908
        case PMIX_DSTTOP:       r = d * (s * a) + s * (255 - (d * a)); break;                   // DstATop
10909
        case PMIX_SRCTOP:       r = s * (d * a) + d * (255 - (s * a)); break;                   // SrcATop
10910
        case PMIX_SRCOVER:      r = s + (255 - (s * a)) * d; break;                             // SrcOver
10911
        case PMIX_SCREEN:       r = s + d - s * d; break;                                       // Screen
10912
    }
10913
 
10914
    return r & 0x00ff;
10915
}
10916
 
10917
bool TButton::isPassThrough()
10918
{
10919
    DECL_TRACER("TButton::isPassThrough()");
10920
 
10921
    if (hs.empty())
10922
        return false;
10923
 
10924
    if (strCaseCompare(hs, "passThru") == 0)
10925
        return true;
10926
 
10927
    return false;
10928
}
10929
 
10930
/**
10931
 * @brief Flip the red and blue color level
10932
 * Swaps the red and blue color level. This is sometimes necessary to preserve
10933
 * the correct color.
10934
 * This method changes the content of the parameter \b color to the swapped
10935
 * value!
10936
 *
10937
 * @param color     The color to swap.
10938
 * @return Returns the the color where red and blue level was swapped.
10939
 */
10940
SkColor& TButton::flipColorLevelsRB(SkColor& color)
10941
{
10942
    DECL_TRACER("TButton::flipColorLevelsRB(SkColor& color)");
10943
 
10944
    SkColor red = SkColorGetR(color);
10945
    SkColor green = SkColorGetG(color);
10946
    SkColor blue = SkColorGetB(color);
10947
    SkColor alpha = SkColorGetA(color);
10948
    color = SkColorSetARGB(alpha, blue, green, red);
10949
    return color;
10950
}
10951
 
10952
void TButton::runBargraphMove(int distance, bool moveUp)
10953
{
10954
    DECL_TRACER("TButton::runBargraphMove(int distance, bool moveUp)");
10955
 
10956
    if (mThreadRunMove)
10957
        return;
10958
 
10959
    mRunBargraphMove = true;
10960
 
10961
    try
10962
    {
10963
        mThrSlider = thread([=] { threadBargraphMove(distance, moveUp); });
10964
        mThrSlider.detach();
10965
    }
10966
    catch (std::exception& e)
10967
    {
10968
        MSG_ERROR("Error starting thread: " << e.what());
10969
        mRunBargraphMove = false;
10970
        mThreadRunMove = false;
10971
    }
10972
}
10973
 
10974
void TButton::threadBargraphMove(int distance, bool moveUp)
10975
{
10976
    DECL_TRACER("TButton::threadBargraphMove(int distance, bool moveUp)");
10977
 
10978
    if (mThreadRunMove)
10979
        return;
10980
 
10981
    mThreadRunMove = true;
10982
    TButtonStates *buttonStates = getButtonState();
10983
    int lLevel = 0;
10984
    int lastSendLevelX = 0;
10985
    int lastSendLevelY = 0;
10986
 
10987
    if (buttonStates)
10988
    {
10989
        lLevel = buttonStates->getLastLevel();
10990
        lastSendLevelX = buttonStates->getLastSendLevelX();
10991
        lastSendLevelY = buttonStates->getLastSendLevelY();
10992
    }
10993
 
10994
    int ispeed = (moveUp ? lu : ld);
10995
 
10996
    if (ispeed <= 0)
10997
        ispeed = 1;
10998
 
10999
    ispeed *= 100;    // Time is 1/10 of seconds but we need milliseconds
11000
    double speed = static_cast<double>(ispeed);
11001
    double total = static_cast<double>(distance) * speed;
11002
    double step = 1.0;
11003
    double pos = 0.0;
11004
    double lastLevel = static_cast<double>(lLevel);
11005
    double posLevel = lastLevel;
11006
 
11007
    if (step <= 0.0)
11008
        step = 1.0;
11009
 
11010
    std::chrono::milliseconds ms = std::chrono::milliseconds(static_cast<long>(total));
11011
    std::chrono::milliseconds msStep = std::chrono::milliseconds(static_cast<long>(step));
11012
    MSG_DEBUG("step: " << step << ", total time (ms): " << total << ", distance: " << distance << ", speed: " << speed);
11013
 
11014
    for (std::chrono::milliseconds mi = std::chrono::milliseconds(0); mi < ms; mi += msStep)
11015
    {
11016
        if (!mRunBargraphMove)
11017
            break;
11018
 
11019
        lastLevel = (moveUp ? (posLevel - pos) : (posLevel + pos));
11020
 
11021
        if (static_cast<int>(lastLevel) != lLevel)
11022
        {
11023
            int level = static_cast<int>(lastLevel);
11024
 
11025
            if (!drawBargraph(mActInstance, level))
11026
                break;
11027
 
11028
            if (lp && lv && gPageManager && gPageManager->getLevelSendState())
11029
            {
11030
                amx::ANET_SEND scmd;
11031
                scmd.device = TConfig::getChannel();
11032
                scmd.port = lp;
11033
                scmd.channel = lv;
11034
                scmd.level = lv;
11035
                scmd.value = (ri ? ((rh - rl) - level) : level);
11036
                scmd.MC = 0x008a;
11037
 
11038
                if (gAmxNet)
11039
                {
11040
                    if (lastSendLevelX != level)
11041
                        gAmxNet->sendCommand(scmd);
11042
 
11043
                    lastSendLevelX = level;
11044
 
11045
                    if (buttonStates)
11046
                        buttonStates->setLastSendLevelX(level);
11047
                }
11048
            }
11049
        }
11050
 
11051
        if (pos >= static_cast<double>(distance))
11052
            break;
11053
 
11054
        pos += step;
11055
        std::this_thread::sleep_for(std::chrono::milliseconds(msStep));
11056
    }
11057
 
11058
    mThreadRunMove = false;
11059
}
11060
 
11061
TButtonStates *TButton::getButtonState()
11062
{
11063
    DECL_TRACER("TButton::getButtonState()");
11064
 
11065
    if (!gPageManager)
11066
        return nullptr;
11067
 
11068
    TButtonStates *s = gPageManager->getButtonState(type, mButtonID);
11069
    MSG_DEBUG("Found button ID: " << getButtonIDstr(s->getID()) << ", type: " << buttonTypeToString(s->getType()) << ", lastLevel: " << s->getLastLevel() << ", lastJoyX: " << s->getLastJoyX() << ", lasJoyY: " << s->getLastJoyY());
11070
    return s;
11071
}
11072
 
11073
int TButton::getLevelValue()
11074
{
11075
    DECL_TRACER("TButton::getLevelValue()");
11076
 
11077
    TButtonStates *buttonStates = getButtonState();
11078
 
11079
    if (!buttonStates)
11080
    {
11081
        MSG_ERROR("Button states not found!");
11082
        return 0;
11083
    }
11084
 
11085
    int level = buttonStates->getLastLevel();
11086
 
11087
    if (ri > 0)
11088
        level = (rh - rl) - level;
11089
 
11090
    return level;
11091
}
11092
 
11093
void TButton::setLevelValue(int level)
11094
{
11095
    DECL_TRACER("TButton::setLevelValue(int level)");
11096
 
11097
    if (level < rl || level > rh)
11098
        return;
11099
 
11100
    TButtonStates *buttonStates = getButtonState();
11101
 
11102
    if (!buttonStates)
11103
        return;
11104
 
11105
    buttonStates->setLastLevel(level);
11106
}
11107
 
11108
int TButton::getLevelAxisX()
11109
{
11110
    DECL_TRACER("TButton::getLevelAxisX()");
11111
 
11112
    TButtonStates *buttonStates = getButtonState();
11113
 
11114
    if (!buttonStates)
11115
    {
11116
        MSG_ERROR("Button states not found!");
11117
        return 0;
11118
    }
11119
 
11120
    int level = buttonStates->getLastJoyX();
11121
 
11122
    if (ri > 0)
11123
        level = (rh - rl) - level;
11124
 
11125
    return level;
11126
}
11127
 
11128
int TButton::getLevelAxisY()
11129
{
11130
    DECL_TRACER("TButton::getLevelAxisY()");
11131
 
11132
    TButtonStates *buttonStates = getButtonState();
11133
 
11134
    if (!buttonStates)
11135
    {
11136
        MSG_ERROR("Button states not found!");
11137
        return 0;
11138
    }
11139
 
11140
    int level = buttonStates->getLastJoyY();
11141
 
11142
    if (ji > 0)
11143
        level = (rh - rl) - level;
11144
 
11145
    return level;
11146
}
11147
 
11148
string TButton::getButtonIDstr(uint32_t rid)
11149
{
11150
    uint32_t id = (rid == 0x1fffffff ? mButtonID : rid);
11151
    std::stringstream s;
11152
    s << std::setfill('0') << std::setw(8) << std::hex << id;
11153
    return s.str();
11154
}
11155
 
11156
bool TButton::setListSource(const string &source, const vector<string>& configs)
11157
{
11158
    DECL_TRACER("TButton::setListSource(const string &source, const vector<string>& configs)");
11159
 
11160
    TUrl url;
11161
 
11162
    listSourceUser.clear();
11163
    listSourcePass.clear();
11164
    listSourceCsv = false;
11165
    listSourceHasHeader = false;
11166
 
11167
    if (configs.size() > 0)
11168
    {
11169
        vector<string>::const_iterator iter;
11170
 
11171
        for (iter = configs.begin(); iter != configs.end(); ++iter)
11172
        {
11173
            size_t pos;
11174
 
11175
            if ((pos = iter->find("user=")) != string::npos)
11176
                listSourceUser = iter->substr(pos + 5);
11177
            else if ((pos = iter->find("pass=")) != string::npos)
11178
                listSourcePass = iter->substr(pos + 5);
11179
            else if (iter->find("csv=") != string::npos)
11180
            {
11181
                string str = *iter;
11182
                string low = toLower(str);
11183
 
11184
                if (low.find("true") != string::npos || low.find("1") != string::npos)
11185
                    listSourceCsv = true;
11186
            }
11187
            else if (iter->find("has_header=") != string::npos)
11188
            {
11189
                string str = *iter;
11190
                string low = toLower(str);
11191
 
11192
                if (low.find("true") != string::npos || low.find("1") != string::npos)
11193
                    listSourceHasHeader = true;
11194
            }
11195
        }
11196
    }
11197
 
11198
    if (!url.setUrl(source))    // Dynamic source?
11199
    {
11200
        size_t idx = 0;
11201
 
11202
        if (!gPrjResources)
11203
            return false;
11204
 
11205
        if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
11206
        {
11207
            MSG_ERROR("There exists no image resource!");
11208
            return false;
11209
        }
11210
 
482 andreas 11211
        RESOURCE_T resource = gPrjResources->findResource(static_cast<int>(idx), source);
446 andreas 11212
 
11213
        if (resource.protocol.empty())
11214
        {
11215
            MSG_WARNING("Resource " << source << " not found!");
11216
            return false;
11217
        }
11218
 
11219
        listSource = resource.protocol + "://";
11220
 
11221
        if (!resource.user.empty() || !listSourceUser.empty())
11222
        {
11223
            listSource += ((listSourceUser.empty() == false) ? listSourceUser : resource.user);
11224
 
11225
            if ((!resource.password.empty() && !resource.encrypted) || !listSourcePass.empty())
11226
                listSource += ":" + ((listSourcePass.empty() == false) ? listSourcePass : resource.password);
11227
 
11228
            listSource += "@";
11229
        }
11230
 
11231
        listSource += resource.host;
11232
 
11233
        if (!resource.path.empty())
11234
            listSource += "/" + resource.path;
11235
 
11236
        if (!resource.file.empty())
11237
            listSource += "/" + resource.file;
11238
 
11239
        return true;
11240
    }
11241
 
11242
    listSource = source;
11243
    return true;
11244
}
11245
 
11246
bool TButton::setListSourceFilter(const string& filter)
11247
{
11248
    DECL_TRACER("TButton::setListSourceFilter(const string& filter)");
11249
 
11250
    if (filter.empty())
11251
        return false;
11252
 
11253
    listFilter = filter;
11254
    MSG_DEBUG("listSourceFilter: " << listFilter);
11255
    return true;
11256
}
11257
 
11258
void TButton::setListViewColumns(int cols)
11259
{
11260
    DECL_TRACER("TButton::setListViewColumns(int cols)");
11261
 
11262
    if (cols <= 0)
11263
        return;
11264
 
11265
    tc = cols;
11266
}
11267
 
11268
void TButton::setListViewLayout(int layout)
11269
{
11270
    DECL_TRACER("TButton::setListViewLayout(int layout)");
11271
 
11272
    if (layout < 1 || layout > 6)
11273
        return;
11274
 
11275
    listLayout = layout;
11276
}
11277
 
11278
void TButton::setListViewComponent(int comp)
11279
{
11280
    DECL_TRACER("TButton::setListViewComponent(int comp)");
11281
 
11282
    if (comp < 0 || comp > 7)
11283
        return;
11284
 
11285
    listComponent = comp;
11286
}
11287
 
11288
void TButton::setListViewCellheight(int height, bool percent)
11289
{
11290
    DECL_TRACER("TButton::setListViewCellheight(int height, bool percent)");
11291
 
11292
    int minHeight = ht / tr;    // Total height / number of rows
11293
    int maxHeight = (int)((double)ht / 100.0 * 95.0);
11294
 
11295
    if (!percent && (height < minHeight || height > maxHeight))
11296
        return;
11297
 
11298
    if (percent)
11299
    {
11300
        int h = (int)((double)ht / 100.0 * (double)height);
11301
 
11302
        if (h >= minHeight && h <= maxHeight)
11303
            tj = h;
11304
 
11305
        return;
11306
    }
11307
 
11308
    tj = height;
11309
}
11310
 
11311
void TButton::setListViewFilterHeight(int height, bool percent)
11312
{
11313
    DECL_TRACER("TButton::setListViewFilterHeight(int height, bool percent)");
11314
 
11315
    if (percent && (height < 5 || height > 25))
11316
        return;
11317
 
11318
    if (!percent && height < 24)
11319
        return;
11320
 
11321
    if (percent)
11322
    {
11323
        listViewColFilterHeight = (int)((double)ht / 100.0 * (double)height);
11324
        return;
11325
    }
11326
    else
11327
    {
11328
        int maxHeight = (int)((double)ht / 100.0 * 25.0);
11329
 
11330
        if (height < maxHeight)
11331
            listViewColFilterHeight = height;
11332
    }
11333
}
11334
 
11335
void TButton::setListViewP1(int p1)
11336
{
11337
    DECL_TRACER("TButton::setListViewP1(int p1)");
11338
 
11339
    if (p1 < 10 || p1 > 90)
11340
        return;
11341
 
11342
    listViewP1 = p1;
11343
}
11344
 
11345
void TButton::setListViewP2(int p2)
11346
{
11347
    DECL_TRACER("TButton::setListViewP2(int p2)");
11348
 
11349
    if (p2 < 10 || p2 > 90)
11350
        return;
11351
 
11352
    listViewP2 = p2;
11353
}
11354
 
11355
void TButton::listViewNavigate(const string &command, bool select)
11356
{
11357
    DECL_TRACER("TButton::listViewNavigate(const string &command, bool select)");
11358
 
11359
    string cmd = command;
11360
    string upCmd = toUpper(cmd);
11361
 
11362
    if (upCmd != "T" && upCmd != "B" && upCmd != "D" && upCmd != "U" && !isNumeric(upCmd, true))
11363
        return;
11364
 
11365
    // TODO: Add code to navigate a list
11366
    MSG_WARNING("ListView navigation is not supported!" << " [" << upCmd << ", " << (select ? "TRUE" : "FALSE") << "]");
11367
}
11368
 
11369
void TButton::listViewRefresh(int interval, bool force)
11370
{
11371
    DECL_TRACER("TButton::listViewRefresh(int interval, bool force)");
11372
 
11373
    Q_UNUSED(interval);
11374
    Q_UNUSED(force);
11375
 
11376
    // TODO: Add code to load list data and display / refresh them
11377
}
11378
 
11379
void TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)
11380
{
11381
    DECL_TRACER("TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)");
11382
 
11383
    Q_UNUSED(columns);
11384
    Q_UNUSED(order);
11385
    Q_UNUSED(override);
11386
 
11387
    // TODO: Insert code to sort the data in the list
11388
}
11389