Subversion Repositories tpanel

Rev

Rev 482 | Details | Compare with Previous | Last modification | View Log | RSS feed

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