Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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