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)
2834
            gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), mHandle);
195 andreas 2835
 
2836
        mSystemReg = true;
23 andreas 2837
    }
206 andreas 2838
    else if (lp == 0 && lv == SYSTEM_ITEM_CONNSTRENGTH)       // Network connection strength
36 andreas 2839
    {
2840
        if (gPageManager)
2841
            gPageManager->regCallbackNetState(bind(&TButton::funcNetworkState, this, std::placeholders::_1), mHandle);
195 andreas 2842
 
2843
        mSystemReg = true;
36 andreas 2844
    }
206 andreas 2845
    else if (lp == 0 && lv == SYSTEM_ITEM_SYSVOLUME)        // System volume
192 andreas 2846
    {
2847
        mLastLevel = TConfig::getSystemVolume();
2848
        mChanged = true;
195 andreas 2849
        mSystemReg = true;
192 andreas 2850
    }
195 andreas 2851
    else if (cp == 0 && type == GENERAL && ch > 0 && isSystemCheckBox(ch))
192 andreas 2852
    {
195 andreas 2853
        int inst = getButtonInstance(0, ch);
2854
 
2855
        if (inst >= 0)
2856
        {
2857
            mActInstance = inst;
2858
            mChanged = true;
2859
            mSystemReg = true;
2860
        }
192 andreas 2861
    }
195 andreas 2862
    else if (ap == 0 && ad > 0 && isSystemTextLine(ad))
192 andreas 2863
    {
195 andreas 2864
        sr[0].te = sr[1].te = fillButtonText(ad, 0);
185 andreas 2865
        mChanged = true;
195 andreas 2866
        mSystemReg = true;
185 andreas 2867
    }
15 andreas 2868
}
2869
 
2870
void TButton::addPushFunction(string& func, string& page)
2871
{
2872
    DECL_TRACER("TButton::addPushFunction(string& func, string& page)");
2873
 
2874
    vector<string> allFunc = { "Stan", "Prev", "Show", "Hide", "Togg", "ClearG", "ClearP", "ClearA" };
2875
    vector<string>::iterator iter;
2876
 
2877
    for (iter = allFunc.begin(); iter != allFunc.end(); ++iter)
2878
    {
162 andreas 2879
        if (strCaseCompare(*iter, func) == 0)
15 andreas 2880
        {
2881
            bool found = false;
2882
            vector<PUSH_FUNC_T>::iterator iterPf;
2883
 
162 andreas 2884
            if (pushFunc.size() > 0)
15 andreas 2885
            {
162 andreas 2886
                for (iterPf = pushFunc.begin(); iterPf != pushFunc.end(); ++iterPf)
15 andreas 2887
                {
162 andreas 2888
                    if (strCaseCompare(iterPf->pfType, func) == 0)
2889
                    {
2890
                        iterPf->pfName = page;
2891
                        found = true;
2892
                        break;
2893
                    }
15 andreas 2894
                }
2895
            }
2896
 
2897
            if (!found)
2898
            {
2899
                PUSH_FUNC_T pf;
2900
                pf.pfType = func;
2901
                pf.pfName = page;
2902
                pushFunc.push_back(pf);
2903
            }
108 andreas 2904
 
2905
            break;
15 andreas 2906
        }
2907
    }
2908
}
2909
 
16 andreas 2910
void TButton::clearPushFunction(const string& action)
2911
{
2912
    DECL_TRACER("TButton::clearPushFunction(const string& action)");
2913
 
2914
    if (pushFunc.empty())
2915
        return;
2916
 
2917
    vector<PUSH_FUNC_T>::iterator iter;
2918
 
118 andreas 2919
    for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
16 andreas 2920
    {
162 andreas 2921
        if (strCaseCompare(iter->pfName, action) == 0)
16 andreas 2922
        {
2923
            pushFunc.erase(iter);
2924
            return;
2925
        }
2926
    }
2927
}
2928
 
8 andreas 2929
void TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)
4 andreas 2930
{
8 andreas 2931
    DECL_TRACER("TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)");
4 andreas 2932
 
8 andreas 2933
    if (!order)
2934
        return;
2935
 
2936
    if (sdo.empty() || sdo.length() != 10)
4 andreas 2937
    {
8 andreas 2938
        *order     = ORD_ELEM_FILL;
2939
        *(order+1) = ORD_ELEM_BITMAP;
2940
        *(order+2) = ORD_ELEM_ICON;
2941
        *(order+3) = ORD_ELEM_TEXT;
2942
        *(order+4) = ORD_ELEM_BORDER;
2943
        return;
2944
    }
2945
 
2946
    int elems = sdo.length() / 2;
2947
 
2948
    for (int i = 0; i < elems; i++)
2949
    {
2950
        int e = atoi(sdo.substr(i * 2, 2).c_str());
2951
 
2952
        if (e < 1 || e > 5)
2953
        {
2954
            MSG_ERROR("Invalid draw order \"" << sdo << "\"!");
2955
            TError::setError();
2956
            return;
2957
        }
2958
 
2959
        *(order+i) = (DRAW_ORDER)e;
2960
    }
2961
}
2962
 
2963
bool TButton::buttonFill(SkBitmap* bm, int instance)
2964
{
2965
    DECL_TRACER("TButton::buttonFill(SkBitmap* bm, int instance)");
2966
 
2967
    if (!bm)
2968
    {
2969
        MSG_ERROR("Invalid bitmap!");
4 andreas 2970
        return false;
2971
    }
2972
 
165 andreas 2973
    if (instance < 0 || (size_t)instance >= sr.size())
2974
    {
2975
        MSG_ERROR("Invalid instance " << instance);
2976
        return false;
2977
    }
2978
 
8 andreas 2979
    SkColor color = TColor::getSkiaColor(sr[instance].cf);
137 andreas 2980
    MSG_DEBUG("Fill color[" << instance << "]: " << sr[instance].cf << " (#" << std::setw(8) << std::setfill('0') << std::hex << color << ")");
8 andreas 2981
    bm->eraseColor(color);
2982
    return true;
2983
}
2984
 
46 andreas 2985
bool TButton::buttonBitmap(SkBitmap* bm, int inst)
8 andreas 2986
{
2987
    DECL_TRACER("TButton::buttonBitmap(SkBitmap* bm, int instane)");
2988
 
240 andreas 2989
    if (prg_stopped || !bm)
33 andreas 2990
        return false;
35 andreas 2991
 
46 andreas 2992
    int instance = inst;
2993
 
2994
    if (inst < 0)
2995
        instance = 0;
2996
    else if ((size_t)inst >= sr.size())
2997
        instance = sr.size() - 1;
2998
 
184 andreas 2999
    /*
3000
     * Here we test if we have a cameleon image. If there is a mask (sr[].mi)
3001
     * and no frame (sr[].bs) then we have a cameleon image. A bitmap is
3002
     * optional. If there is one it will be used to draw with the mask.
3003
     * Otherwise the mask may be used as an overlay for a bitmap on another
3004
     * button below the mask.
3005
     */
29 andreas 3006
    if (!sr[instance].mi.empty() && sr[instance].bs.empty())       // Chameleon image?
4 andreas 3007
    {
184 andreas 3008
        MSG_DEBUG("Chameleon image consisting of mask " << sr[instance].mi << " and bitmap " << (sr[instance].bm.empty() ? "NONE" : sr[instance].bm) << " ...");
69 andreas 3009
 
165 andreas 3010
        SkBitmap bmMi;
3011
        SkBitmap bmBm;
3012
 
167 andreas 3013
        if (!TImgCache::getBitmap(sr[instance].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[instance].mi_width, &sr[instance].mi_height))
69 andreas 3014
        {
165 andreas 3015
            sk_sp<SkData> data = readImage(sr[instance].mi);
3016
            bool loaded = false;
3017
 
3018
            if (data)
3019
            {
3020
                DecodeDataToBitmap(data, &bmMi);
3021
 
3022
                if (!bmMi.empty())
3023
                {
3024
                    TImgCache::addImage(sr[instance].mi, bmMi, _BMTYPE_CHAMELEON);
3025
                    loaded = true;
3026
                    sr[instance].mi_width = bmMi.info().width();
3027
                    sr[instance].mi_height = bmMi.info().height();
3028
                }
3029
            }
3030
 
3031
            if(!loaded)
3032
            {
3033
                MSG_ERROR("Missing image " << sr[instance].mi << "!");
3034
                TError::setError();
3035
                return false;
3036
            }
69 andreas 3037
        }
3038
 
240 andreas 3039
        MSG_DEBUG("Chameleon image size: " << bmMi.info().width() << " x " << bmMi.info().height());
165 andreas 3040
        SkBitmap imgRed(bmMi);
24 andreas 3041
        SkBitmap imgMask;
163 andreas 3042
        bool haveBothImages = true;
4 andreas 3043
 
24 andreas 3044
        if (!sr[instance].bm.empty())
163 andreas 3045
        {
167 andreas 3046
            if (!TImgCache::getBitmap(sr[instance].bm, &bmBm, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
163 andreas 3047
            {
165 andreas 3048
                sk_sp<SkData> data = readImage(sr[instance].bm);
3049
                bool loaded = false;
3050
 
3051
                if (data)
3052
                {
3053
                    DecodeDataToBitmap(data, &bmBm);
3054
 
3055
                    if (!bmMi.empty())
3056
                    {
3057
                        TImgCache::addImage(sr[instance].bm, bmMi, _BMTYPE_BITMAP);
3058
                        loaded = true;
3059
                        sr[instance].bm_width = bmBm.info().width();
3060
                        sr[instance].bm_height = bmBm.info().height();
3061
                    }
3062
                }
3063
 
3064
                if (!loaded)
3065
                {
3066
                    MSG_ERROR("Missing image " << sr[instance].bm << "!");
3067
                    TError::setError();
3068
                    return false;
3069
                }
163 andreas 3070
            }
165 andreas 3071
 
184 andreas 3072
            if (!bmBm.empty())
240 andreas 3073
            {
3074
                if (!imgMask.installPixels(bmBm.pixmap()))
3075
                {
3076
                    MSG_ERROR("Error installing pixmap " << sr[instance].bm << " for chameleon image!");
3077
                    imgMask.allocN32Pixels(imgRed.info().width(), imgRed.info().height());
3078
                    imgMask.eraseColor(SK_ColorTRANSPARENT);
3079
                    haveBothImages = false;
3080
                }
3081
            }
163 andreas 3082
            else
3083
            {
184 andreas 3084
                MSG_WARNING("No or invalid bitmap! Ignoring bitmap for cameleon image.");
163 andreas 3085
                imgMask.allocN32Pixels(imgRed.info().width(), imgRed.info().height());
3086
                imgMask.eraseColor(SK_ColorTRANSPARENT);
3087
                haveBothImages = false;
3088
            }
3089
        }
3090
        else
3091
            haveBothImages = false;
24 andreas 3092
 
240 andreas 3093
        MSG_DEBUG("Bitmap image size: " << bmBm.info().width() << " x " << bmBm.info().height());
3094
        MSG_DEBUG("Bitmap mask size: " << imgMask.info().width() << " x " << imgMask.info().height());
167 andreas 3095
        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 3096
 
3097
        if (img.empty())
4 andreas 3098
        {
20 andreas 3099
            MSG_ERROR("Error creating the cameleon image \"" << sr[instance].mi << "\" / \"" << sr[instance].bm << "\"!");
3100
            TError::setError();
3101
            return false;
3102
        }
4 andreas 3103
 
240 andreas 3104
        MSG_DEBUG("Have both images: " << (haveBothImages ? "YES" : "NO"));
20 andreas 3105
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3106
        SkImageInfo info = img.info();
3107
        SkPaint paint;
24 andreas 3108
        paint.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 3109
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgMask);
3110
        ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
4 andreas 3111
 
20 andreas 3112
        POSITION_t position = calcImagePosition(sr[instance].mi_width, sr[instance].mi_height, SC_BITMAP, instance);
7 andreas 3113
 
20 andreas 3114
        if (!position.valid)
3115
        {
3116
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
3117
            TError::setError();
3118
            return false;
4 andreas 3119
        }
10 andreas 3120
 
20 andreas 3121
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3122
        paint.setBlendMode(SkBlendMode::kSrc);
21 andreas 3123
 
3124
        if (sr[instance].sb == 0)
163 andreas 3125
        {
3126
            if (!haveBothImages)
3127
            {
179 andreas 3128
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(img);
3129
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
163 andreas 3130
 
3131
                if (!sr[instance].bm.empty())
3132
                {
165 andreas 3133
                    imgMask.installPixels(bmBm.pixmap());
163 andreas 3134
                    paint.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 3135
                    _image = SkImage::MakeFromBitmap(imgMask);
3136
                    can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
163 andreas 3137
                }
3138
            }
3139
            else
179 andreas 3140
            {
3141
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(img);
3142
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
3143
            }
163 andreas 3144
        }
21 andreas 3145
        else    // Scale to fit
3146
        {
163 andreas 3147
            if (!haveBothImages)
3148
            {
3149
                SkRect rect;
3150
                rect.setXYWH(0, 0, imgRed.info().width(), imgRed.info().height());
3151
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(img);
179 andreas 3152
                can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
163 andreas 3153
 
3154
                if (!sr[instance].bm.empty())
3155
                {
165 andreas 3156
                    imgMask.installPixels(bmBm.pixmap());
163 andreas 3157
                    rect.setXYWH(position.left, position.top, position.width, position.height);
3158
                    im = SkImage::MakeFromBitmap(imgMask);
3159
                    paint.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 3160
                    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
163 andreas 3161
                }
3162
            }
3163
            else
3164
            {
3165
                SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
3166
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(img);
179 andreas 3167
                can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
163 andreas 3168
            }
21 andreas 3169
        }
4 andreas 3170
    }
3171
    else if (!sr[instance].bm.empty())
3172
    {
69 andreas 3173
        MSG_TRACE("Drawing normal image " << sr[instance].bm << " ...");
3174
 
165 andreas 3175
        SkBitmap image;
3176
 
167 andreas 3177
        if (!TImgCache::getBitmap(sr[instance].bm, &image, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
69 andreas 3178
        {
165 andreas 3179
            sk_sp<SkData> data = readImage(sr[instance].bm);
3180
            bool loaded = false;
3181
 
3182
            if (data)
3183
            {
3184
                DecodeDataToBitmap(data, &image);
3185
 
3186
                if (!image.empty())
3187
                {
3188
                    TImgCache::addImage(sr[instance].mi, image, _BMTYPE_BITMAP);
3189
                    loaded = true;
3190
                    sr[instance].bm_width = image.info().width();
3191
                    sr[instance].bm_height = image.info().height();
3192
                }
3193
            }
3194
 
3195
            if (!loaded)
3196
            {
3197
                MSG_ERROR("Missing image " << sr[instance].bm << "!");
3198
                return true;        // We want the button even without an image
3199
            }
69 andreas 3200
        }
3201
 
6 andreas 3202
        if (image.empty())
4 andreas 3203
        {
3204
            MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
3205
            TError::setError();
3206
            return false;
3207
        }
3208
 
99 andreas 3209
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
3210
        POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
3211
//        POSITION_t position = calcImagePosition(sr[instance].bm_width, sr[instance].bm_height, SC_BITMAP, instance);
4 andreas 3212
 
3213
        if (!position.valid)
3214
        {
3215
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
3216
            TError::setError();
3217
            return false;
3218
        }
3219
 
10 andreas 3220
        MSG_DEBUG("Putting bitmap on top of image ...");
3221
        SkPaint paint;
69 andreas 3222
        paint.setBlendMode(SkBlendMode::kSrcOver);
3223
        SkCanvas can(*bm, SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kUnknown_SkPixelGeometry));
21 andreas 3224
 
69 andreas 3225
        if (sr[instance].sb == 0)   // Scale bitmap?
3226
        {                           // No, keep size
46 andreas 3227
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
179 andreas 3228
            {
3229
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(image);
3230
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
3231
            }
49 andreas 3232
            else    // We need only a subset of the image
46 andreas 3233
            {
3234
                MSG_DEBUG("Create a subset of an image ...");
3235
 
3236
                // Create a new Info to have the size of the subset.
3237
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
3238
                size_t byteSize = info.computeMinByteSize();
3239
 
3240
                if (byteSize == 0)
3241
                {
3242
                    MSG_ERROR("Unable to calculate size of image!");
3243
                    TError::setError();
3244
                    return false;
3245
                }
3246
 
3247
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
3248
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
3249
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
3250
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
179 andreas 3251
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(part);
3252
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
46 andreas 3253
            }
3254
        }
21 andreas 3255
        else    // Scale to fit
3256
        {
99 andreas 3257
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
21 andreas 3258
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
179 andreas 3259
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
21 andreas 3260
        }
6 andreas 3261
    }
8 andreas 3262
    else
6 andreas 3263
    {
8 andreas 3264
        MSG_DEBUG("No bitmap defined.");
3265
    }
4 andreas 3266
 
8 andreas 3267
    return true;
3268
}
4 andreas 3269
 
97 andreas 3270
bool TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)
21 andreas 3271
{
97 andreas 3272
    DECL_TRACER("TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)");
21 andreas 3273
 
33 andreas 3274
    if (prg_stopped)
3275
        return false;
35 andreas 3276
 
21 andreas 3277
    if (!gPrjResources)
3278
    {
3279
        MSG_ERROR("Internal error: Global resource class not initialized!");
3280
        return false;
3281
    }
3282
 
165 andreas 3283
    if (instance < 0 || (size_t)instance >= sr.size())
3284
    {
3285
        MSG_ERROR("Invalid instance " << instance);
3286
        return false;
3287
    }
3288
 
21 andreas 3289
    if (!sr[instance].dynamic)
3290
    {
3291
        MSG_WARNING("Button " << bi << ": \"" << na << "\" is not for remote image!");
3292
        return false;
3293
    }
3294
 
98 andreas 3295
    if (!visible)
99 andreas 3296
    {
3297
        MSG_DEBUG("Dynamic button " << TObject::handleToString(mHandle) << " is invisible. Will not draw it.");
98 andreas 3298
        return true;
99 andreas 3299
    }
98 andreas 3300
 
99 andreas 3301
    MSG_DEBUG("Dynamic button " << TObject::handleToString(mHandle) << " will be drawn ...");
97 andreas 3302
    size_t idx = 0;
21 andreas 3303
 
97 andreas 3304
    if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
3305
    {
3306
        MSG_ERROR("There exists no image resource!");
3307
        return false;
3308
    }
3309
 
3310
    RESOURCE_T resource = gPrjResources->findResource(idx, sr[instance].bm);
3311
 
21 andreas 3312
    if (resource.protocol.empty())
3313
    {
100 andreas 3314
        MSG_WARNING("Resource " << sr[instance].bm << " not found!");
3315
        return true;
21 andreas 3316
    }
3317
 
3318
    string path = resource.path;
3319
 
3320
    if (!resource.file.empty())
3321
        path += "/" + resource.file;
3322
 
94 andreas 3323
    string url = THTTPClient::makeURLs(toLower(resource.protocol), resource.host, 0, path);
21 andreas 3324
 
97 andreas 3325
    if (url.empty())
3326
    {
100 andreas 3327
        MSG_DEBUG("No URL, no bitmap!");
97 andreas 3328
        return true;    // We have no image but the button still exists
3329
    }
3330
 
3331
    SkBitmap image;
3332
 
165 andreas 3333
    if (TImgCache::getBitmap(url, &image, _BMTYPE_URL))
97 andreas 3334
    {
100 andreas 3335
        MSG_DEBUG("Found image \"" << url << "\" in the cache. Will reuse it.");
99 andreas 3336
        IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
3337
        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 3338
 
3339
        if (!position.valid)
3340
        {
3341
            MSG_ERROR("Error calculating the position of the image for button number " << bi);
3342
            TError::setError();
3343
            return false;
3344
        }
3345
 
3346
        SkPaint paint;
3347
        paint.setBlendMode(SkBlendMode::kSrcOver);
3348
        SkCanvas can(*bm, SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kUnknown_SkPixelGeometry));
3349
 
3350
        if (sr[instance].sb == 0)   // Scale bitmap?
3351
        {                           // No, keep size
3352
            if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0)  // Draw the full image
179 andreas 3353
            {
3354
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(image);
3355
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
3356
            }
97 andreas 3357
            else    // We need only a subset of the image
3358
            {
3359
                MSG_DEBUG("Create a subset of an image ...");
3360
 
3361
                // Create a new Info to have the size of the subset.
3362
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
3363
                size_t byteSize = info.computeMinByteSize();
3364
 
3365
                if (byteSize == 0)
3366
                {
3367
                    MSG_ERROR("Unable to calculate size of image!");
3368
                    TError::setError();
3369
                    return false;
3370
                }
3371
 
3372
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
3373
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
3374
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
3375
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
179 andreas 3376
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(part);
3377
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
97 andreas 3378
            }
3379
        }
3380
        else    // Scale to fit
3381
        {
99 andreas 3382
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
97 andreas 3383
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
179 andreas 3384
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
97 andreas 3385
        }
3386
 
3387
        return true;
3388
    }
3389
 
21 andreas 3390
    try
3391
    {
94 andreas 3392
        // First me must add the credential for the image into a bitmap cache element
3393
        BITMAP_CACHE bc;
3394
        bc.top = tp;
3395
        bc.left = lt;
3396
        bc.width = wt;
3397
        bc.height = ht;
3398
        bc.bi = bi;
97 andreas 3399
        bc.show = show;
94 andreas 3400
        bc.handle = getHandle();
3401
        bc.parent = getParent();
3402
        bc.bitmap = *bm;
3403
        addToBitmapCache(bc);
97 andreas 3404
 
3405
        if (state)
3406
            *state = true;  // Prevent the calling method from displaying the button
3407
 
21 andreas 3408
        MSG_TRACE("Starting thread for loading a dynamic image ...");
94 andreas 3409
        mThrRes = std::thread([=] { this->funcResource(&resource, url, bc, instance); });
21 andreas 3410
        MSG_TRACE("Thread started. Detaching ...");
3411
        mThrRes.detach();
3412
        MSG_TRACE("Thread is running and detached.");
3413
    }
3414
    catch (std::exception& e)
3415
    {
28 andreas 3416
        MSG_ERROR("Error starting the resource thread: " << e.what());
21 andreas 3417
    }
3418
 
3419
    return true;
3420
}
3421
 
99 andreas 3422
/*
3423
 * Draws the elements of a button starting at the point where the bitmap was
3424
 * already drawed. Everything coming afterwards acording to the draw order
3425
 * is drawed in the desired order.
3426
 * This method is called out of a thread to draw a button with an external
3427
 * image coming from a WEB server.
3428
 */
3429
bool TButton::drawAlongOrder(SkBitmap *imgButton, int instance)
3430
{
3431
    DECL_TRACER("TButton::drawAlongOrder(SkBitmap *imgButton, int instance)");
3432
 
165 andreas 3433
    if (instance < 0 || (size_t)instance >= sr.size())
3434
    {
3435
        MSG_ERROR("Invalid instance " << instance);
3436
        return false;
3437
    }
3438
 
99 andreas 3439
    bool cont = false;
3440
 
3441
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
3442
    {
3443
        if (!cont && mDOrder[i] == ORD_ELEM_BITMAP)
3444
        {
3445
            cont = true;
3446
            continue;
3447
        }
100 andreas 3448
        else if (!cont)
3449
            continue;
99 andreas 3450
 
3451
        if (mDOrder[i] == ORD_ELEM_FILL)
3452
        {
3453
            if (!buttonFill(imgButton, instance))
3454
            {
3455
                mutex_button.unlock();
3456
                return false;
3457
            }
3458
        }
3459
        else if (mDOrder[i] == ORD_ELEM_ICON)
3460
        {
3461
            if (!buttonIcon(imgButton, instance))
3462
            {
3463
                mutex_button.unlock();
3464
                return false;
3465
            }
3466
        }
3467
        else if (mDOrder[i] == ORD_ELEM_TEXT)
3468
        {
3469
            if (!buttonText(imgButton, instance))
3470
            {
3471
                mutex_button.unlock();
3472
                return false;
3473
            }
3474
        }
3475
        else if (mDOrder[i] == ORD_ELEM_BORDER)
3476
        {
3477
            if (!buttonBorder(imgButton, instance))
3478
            {
3479
                mutex_button.unlock();
3480
                return false;
3481
            }
3482
        }
3483
    }
3484
 
3485
    return true;
3486
}
3487
 
94 andreas 3488
void TButton::funcResource(const RESOURCE_T* resource, const std::string& url, BITMAP_CACHE bc, int instance)
21 andreas 3489
{
3490
    DECL_TRACER("TButton::funcResource(RESOURCE_T* resource, std::string& url, SkBitmap* bm, int instance)");
3491
 
97 andreas 3492
    if (prg_stopped || killed || _restart_ || !resource)
35 andreas 3493
        return;
3494
 
21 andreas 3495
    if (resource->refresh > 0 && !resource->dynamo)      // Periodically refreshing image?
3496
    {
3497
        MSG_DEBUG("Retrieving periodicaly refreshed image");
3498
 
97 andreas 3499
        if (!bc.handle || !bc.parent || bc.bi <= 1)
21 andreas 3500
        {
3501
            MSG_ERROR("Invalid button. Can't make a dynamo image!");
3502
            return;
3503
        }
3504
 
97 andreas 3505
        THR_REFRESH_t *thref = _findResource(bc.handle, bc.parent, bc.bi);
21 andreas 3506
        TImageRefresh *mImageRefresh = nullptr;
3507
 
3508
        if (!thref)
3509
        {
3510
            MSG_DEBUG("Creating a new refresh thread");
3511
            mImageRefresh = new TImageRefresh();
3512
            mImageRefresh->registerCallback(bind(&TButton::_imageRefresh, this, std::placeholders::_1));
3513
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
3514
            mImageRefresh->setUsername(resource->user);
3515
            mImageRefresh->setPassword(resource->password);
3516
 
3517
            if (resource->preserve)
3518
                mImageRefresh->setRunOnce();
3519
 
97 andreas 3520
            _addResource(mImageRefresh, bc.handle, bc.parent, bc.bi);
21 andreas 3521
        }
3522
        else
3523
        {
3524
            mImageRefresh = thref->mImageRefresh;
3525
 
3526
            if (!mImageRefresh)
3527
            {
3528
                MSG_ERROR("Error creating a new refresh class!");
3529
                return;
3530
            }
3531
 
3532
            mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
3533
            mImageRefresh->setUsername(resource->user);
3534
            mImageRefresh->setPassword(resource->password);
3535
 
3536
            if (resource->preserve)
3537
                mImageRefresh->setRunOnce();
3538
        }
3539
 
97 andreas 3540
        if (mImageRefresh->isRunning())
3541
            mImageRefresh->stopWait();
3542
 
3543
        if (!mImageRefresh->isRunning() && !_restart_)
21 andreas 3544
        {
3545
            MSG_DEBUG("Starting a refresh thread.");
3546
            mImageRefresh->run(url);
3547
        }
3548
    }
3549
    else if (resource->refresh == 0 && !resource->dynamo)
3550
    {
3551
        MSG_DEBUG("Retrieving single image");
3552
 
94 andreas 3553
        if (bc.handle == 0)
3554
        {
3555
            MSG_ERROR("Invalid bitmap cache!");
3556
            return;
3557
        }
3558
 
165 andreas 3559
        if (instance < 0 || (size_t)instance >= sr.size())
3560
        {
3561
            MSG_ERROR("Invalid instance " << instance);
3562
            return;
3563
        }
3564
 
97 andreas 3565
        // Check whether we have this image already
3566
        SkBitmap bitm;
3567
        bool cached = false;
3568
 
165 andreas 3569
        cached = TImgCache::getBitmap(url, &bitm, _BMTYPE_URL);
94 andreas 3570
        BITMAP_CACHE bmCache = getBCentryByHandle(bc.handle, bc.parent);
3571
 
97 andreas 3572
        if (!cached)    // If the bitmap was not in cache we must load it
94 andreas 3573
        {
97 andreas 3574
            MSG_DEBUG("Image not in cache. Downloading it ...");
3575
            THTTPClient *WEBClient = nullptr;
94 andreas 3576
 
97 andreas 3577
            if (bmCache.handle == 0)
3578
            {
3579
                MSG_ERROR("Couldn't find the handle " << TObject::handleToString(bc.handle) << " in bitmap cache!");
3580
                return;
3581
            }
3582
 
21 andreas 3583
            char *content = nullptr;
3584
            size_t length = 0, contentlen = 0;
94 andreas 3585
            WEBClient = new THTTPClient;
21 andreas 3586
 
94 andreas 3587
            if (!WEBClient || (content = WEBClient->tcall(&length, url, resource->user, resource->password)) == nullptr)
97 andreas 3588
            {
3589
                if (WEBClient)
3590
                    delete WEBClient;
3591
 
3592
                if (bc.show)
3593
                {
3594
                    setReady(bmCache.handle);
3595
                    showBitmapCache();
3596
                }
3597
                else
3598
                    setInvalid(bc.handle);
3599
 
21 andreas 3600
                return;
97 andreas 3601
            }
21 andreas 3602
 
94 andreas 3603
            contentlen = WEBClient->getContentSize();
21 andreas 3604
            MSG_DEBUG("Loaded " << contentlen << " bytes:");
3605
            sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
3606
 
97 andreas 3607
            if (!data || _restart_)
21 andreas 3608
            {
94 andreas 3609
                delete WEBClient;
21 andreas 3610
                MSG_ERROR("Error making image data!");
97 andreas 3611
 
3612
                if (bc.show)
3613
                {
3614
                    setReady(bmCache.handle);
3615
                    showBitmapCache();
3616
                }
3617
                else
3618
                    setInvalid(bc.handle);
3619
 
21 andreas 3620
                return;
3621
            }
3622
 
3623
            SkBitmap image;
3624
 
3625
            if (!DecodeDataToBitmap(data, &image))
3626
            {
97 andreas 3627
                delete WEBClient;
21 andreas 3628
                MSG_ERROR("Error creating an image!");
97 andreas 3629
 
3630
                if (bc.show)
3631
                {
3632
                    setReady(bmCache.handle);
3633
                    showBitmapCache();
3634
                }
3635
                else
3636
                    setInvalid(bc.handle);
3637
 
21 andreas 3638
                return;
3639
            }
3640
 
97 andreas 3641
            // Put this image into the static image cache
165 andreas 3642
            TImgCache::addImage(url, image, _BMTYPE_URL);
97 andreas 3643
            // Make the button complete
94 andreas 3644
            loadImage(&bmCache.bitmap, image, instance);
99 andreas 3645
            drawAlongOrder(&bmCache.bitmap, instance);
94 andreas 3646
            setBCBitmap(bmCache.handle, bmCache.bitmap);
3647
            setReady(bmCache.handle);
3648
            delete WEBClient;
97 andreas 3649
            // Display the image
94 andreas 3650
            showBitmapCache();
21 andreas 3651
            return;
3652
        }
97 andreas 3653
        else
21 andreas 3654
        {
97 andreas 3655
            MSG_DEBUG("Found image in cache. Using it ...");
165 andreas 3656
 
3657
            if (instance < 0 || (size_t)instance >= sr.size())
3658
            {
3659
                MSG_ERROR("Invalid instance " << instance);
3660
                return;
3661
            }
3662
 
97 andreas 3663
            loadImage(&bmCache.bitmap, bitm, instance);
3664
            setInvalid(bc.handle);
94 andreas 3665
 
97 andreas 3666
            if (bc.show && _displayButton)
176 andreas 3667
            {
97 andreas 3668
                _displayButton(bc.handle, bc.parent, (unsigned char *)bmCache.bitmap.getPixels(), bc.width, bc.height, bmCache.bitmap.info().minRowBytes(), bc.left, bc.top);
176 andreas 3669
                mChanged = false;
3670
            }
21 andreas 3671
        }
3672
    }
97 andreas 3673
    else if (!_restart_)
21 andreas 3674
    {
3675
        MSG_DEBUG("Retrieving a video");
3676
 
3677
        if (_playVideo && !prg_stopped)
3678
        {
3679
            ulong parent = (mHandle >> 16) & 0x0000ffff;
3680
            _playVideo(mHandle, parent, lt, tp, wt, ht, url, resource->user, resource->password);
3681
        }
3682
    }
3683
}
3684
 
38 andreas 3685
void TButton::funcBattery(int level, bool charging, int /* chargeType */)
23 andreas 3686
{
38 andreas 3687
    DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
59 andreas 3688
 
23 andreas 3689
    // Battery level is always a bargraph
59 andreas 3690
    if (ap == 0 && ad == 242)       // Not charging
23 andreas 3691
    {
38 andreas 3692
        mEnabled = !charging;
177 andreas 3693
        mChanged = true;
23 andreas 3694
 
3695
        if (!mEnabled && visible)
3696
            hide(true);
3697
        else if (mEnabled)
3698
        {
3699
            visible = true;
38 andreas 3700
            drawBargraph(mActInstance, level, visible);
23 andreas 3701
        }
3702
    }
59 andreas 3703
    else if (ap == 0 && ad == 234)  // Charging
23 andreas 3704
    {
38 andreas 3705
        mEnabled = charging;
177 andreas 3706
        mChanged = true;
23 andreas 3707
 
3708
        if (!mEnabled && visible)
3709
            hide(true);
3710
        else if (mEnabled)
3711
        {
3712
            visible = true;
38 andreas 3713
            drawBargraph(mActInstance, level, visible);
23 andreas 3714
        }
3715
    }
3716
}
3717
 
36 andreas 3718
void TButton::funcNetworkState(int level)
3719
{
3720
    DECL_TRACER("TButton::funcNetworkState(int level)");
3721
 
38 andreas 3722
    if (level >= rl && level <= rh)
3723
    {
3724
        mLastLevel = level;
177 andreas 3725
        mChanged = true;
38 andreas 3726
        drawMultistateBargraph(mLastLevel);
3727
    }
36 andreas 3728
}
3729
 
21 andreas 3730
bool TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)
3731
{
3732
    DECL_TRACER("TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)");
3733
 
94 andreas 3734
    if (!bm)
3735
    {
3736
        MSG_WARNING("Got no image to load!");
3737
        return false;
3738
    }
3739
 
165 andreas 3740
    if (instance < 0 || (size_t)instance >= sr.size())
3741
    {
3742
        MSG_ERROR("Invalid instance " << instance);
3743
        return false;
3744
    }
3745
 
21 andreas 3746
    SkImageInfo info = image.info();
99 andreas 3747
    IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
3748
    POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : info.width()), (sr[instance].sb ? isize.height : info.height()), SC_BITMAP, instance);
3749
//    POSITION_t position = calcImagePosition(info.width(), info.height(), SC_BITMAP, instance);
21 andreas 3750
 
3751
    if (!position.valid)
3752
    {
3753
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
3754
        return false;
3755
    }
3756
 
94 andreas 3757
    MSG_DEBUG("New image position: left=" << position.left << ", top=" << position.top << ", width=" << position.width << ", height=" << position.height);
3758
    MSG_DEBUG("Image size : width=" << info.width() << ", height=" << info.height());
3759
    MSG_DEBUG("Bitmap size: width=" << bm->info().width() << ", height=" << bm->info().height());
21 andreas 3760
    MSG_DEBUG("Putting bitmap on top of image ...");
3761
    SkPaint paint;
3762
    paint.setBlendMode(SkBlendMode::kSrc);
3763
 
3764
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3765
 
3766
    if (sr[instance].sb == 0)
179 andreas 3767
    {
3768
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(image);
3769
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
3770
    }
21 andreas 3771
    else    // Scale to fit
3772
    {
179 andreas 3773
//        paint.setFilterQuality(kHigh_SkFilterQuality);
99 andreas 3774
        SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
21 andreas 3775
        sk_sp<SkImage> im = SkImage::MakeFromBitmap(image);
179 andreas 3776
        can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
21 andreas 3777
    }
3778
 
3779
    return true;
3780
}
3781
 
111 andreas 3782
bool TButton::barLevel(SkBitmap* bm, int, int level)
15 andreas 3783
{
100 andreas 3784
    DECL_TRACER("TButton::barLevel(SkBitmap* bm, int inst, int level)");
15 andreas 3785
 
30 andreas 3786
    if (!sr[0].mi.empty()  && sr[0].bs.empty() && !sr[1].bm.empty())       // Chameleon image?
15 andreas 3787
    {
38 andreas 3788
        MSG_TRACE("Chameleon image ...");
165 andreas 3789
        SkBitmap bmMi, bmBm;
15 andreas 3790
 
167 andreas 3791
        TImgCache::getBitmap(sr[0].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[0].mi_width, &sr[0].mi_height);
3792
        TImgCache::getBitmap(sr[1].bm, &bmBm, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);
165 andreas 3793
        SkBitmap imgRed(bmMi);
3794
        SkBitmap imgMask(bmBm);
3795
 
20 andreas 3796
        SkBitmap img;
3797
        SkPixmap pixmapRed = imgRed.pixmap();
3798
        SkPixmap pixmapMask;
15 andreas 3799
 
20 andreas 3800
        if (!imgMask.empty())
3801
            pixmapMask = imgMask.pixmap();
15 andreas 3802
 
20 andreas 3803
        int width = sr[0].mi_width;
3804
        int height = sr[0].mi_height;
3805
        int startX = 0;
3806
        int startY = 0;
100 andreas 3807
        // Calculation: width / <effective pixels> * level
3808
        // Calculation: height / <effective pixels> * level
20 andreas 3809
        if (dr.compare("horizontal") == 0)
3810
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
3811
        else
3812
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
3813
 
59 andreas 3814
        if (ri && dr.compare("horizontal") == 0)
20 andreas 3815
        {
59 andreas 3816
            startX = sr[0].mi_width - width;
20 andreas 3817
            width = sr[0].mi_width;
59 andreas 3818
        }
3819
        else if (dr.compare("horizontal") != 0)
3820
        {
3821
            startY = sr[0].mi_height - height;
20 andreas 3822
            height = sr[0].mi_height;
3823
        }
15 andreas 3824
 
20 andreas 3825
        img.allocPixels(SkImageInfo::MakeN32Premul(sr[0].mi_width, sr[0].mi_height));
3826
        SkCanvas canvas(img);
3827
        SkColor col1 = TColor::getSkiaColor(sr[1].cf);
3828
        SkColor col2 = TColor::getSkiaColor(sr[1].cb);
15 andreas 3829
 
20 andreas 3830
        for (int ix = 0; ix < sr[0].mi_width; ix++)
3831
        {
3832
            for (int iy = 0; iy < sr[0].mi_height; iy++)
3833
            {
3834
                SkPaint paint;
3835
                SkColor pixel;
15 andreas 3836
 
20 andreas 3837
                if (ix >= startX && ix < width && iy >= startY && iy < height)
15 andreas 3838
                {
20 andreas 3839
                    SkColor pixelRed = pixmapRed.getColor(ix, iy);
3840
                    SkColor pixelMask;
15 andreas 3841
 
20 andreas 3842
                    if (!imgMask.empty())
3843
                        pixelMask = pixmapMask.getColor(ix, iy);
15 andreas 3844
                    else
20 andreas 3845
                        pixelMask = SK_ColorWHITE;
15 andreas 3846
 
20 andreas 3847
                    pixel = baseColor(pixelRed, pixelMask, col1, col2);
15 andreas 3848
                }
20 andreas 3849
                else
3850
                    pixel = SK_ColorTRANSPARENT;
15 andreas 3851
 
20 andreas 3852
                paint.setColor(pixel);
3853
                canvas.drawPoint(ix, iy, paint);
15 andreas 3854
            }
20 andreas 3855
        }
15 andreas 3856
 
20 andreas 3857
        if (img.empty())
3858
        {
3859
            MSG_ERROR("Error creating the cameleon image \"" << sr[0].mi << "\" / \"" << sr[0].bm << "\"!");
3860
            TError::setError();
3861
            return false;
3862
        }
15 andreas 3863
 
20 andreas 3864
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3865
        SkPaint paint;
3866
        paint.setBlendMode(SkBlendMode::kSrcATop);
179 andreas 3867
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgMask);
3868
        ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
15 andreas 3869
 
20 andreas 3870
        POSITION_t position = calcImagePosition(sr[0].mi_width, sr[0].mi_height, SC_BITMAP, 0);
15 andreas 3871
 
20 andreas 3872
        if (!position.valid)
3873
        {
3874
            MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
3875
            TError::setError();
3876
            return false;
15 andreas 3877
        }
3878
 
20 andreas 3879
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3880
        paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 3881
        _image = SkImage::MakeFromBitmap(img);
3882
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
15 andreas 3883
    }
59 andreas 3884
    else if (!sr[0].bm.empty() && !sr[1].bm.empty())
15 andreas 3885
    {
23 andreas 3886
        MSG_TRACE("Drawing normal image ...");
165 andreas 3887
        SkBitmap image1, image2;
3888
 
167 andreas 3889
        TImgCache::getBitmap(sr[0].bm, &image1, _BMTYPE_BITMAP, &sr[0].bm_width, &sr[0].bm_height);   // State when level = 0%
3890
        TImgCache::getBitmap(sr[1].bm, &image2, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);   // State when level = 100%
59 andreas 3891
        SkCanvas can_bm(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
15 andreas 3892
 
59 andreas 3893
 
23 andreas 3894
        if (image1.empty())
15 andreas 3895
        {
23 andreas 3896
            MSG_ERROR("Error creating the image \"" << sr[0].bm << "\"!");
15 andreas 3897
            TError::setError();
3898
            return false;
3899
        }
3900
 
23 andreas 3901
        if (image2.empty())
3902
        {
3903
            MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
3904
            TError::setError();
3905
            return false;
3906
        }
3907
 
3908
        int width = sr[1].bm_width;
3909
        int height = sr[1].bm_height;
15 andreas 3910
        int startX = 0;
3911
        int startY = 0;
3912
 
100 andreas 3913
        // Calculation: width / <effective pixels> * level
3914
        // Calculation: height / <effective pixels> * level
15 andreas 3915
        if (dr.compare("horizontal") == 0)
3916
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
3917
        else
3918
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
3919
 
59 andreas 3920
        if (ri && dr.compare("horizontal") == 0)     // range inverted?
15 andreas 3921
        {
59 andreas 3922
            startX = sr[0].mi_width - width;
15 andreas 3923
            width = sr[0].mi_width;
59 andreas 3924
        }
3925
        else if (dr.compare("horizontal") != 0)
3926
        {
3927
            startY = sr[0].mi_height - height;
15 andreas 3928
            height = sr[0].mi_height;
3929
        }
3930
 
28 andreas 3931
        MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
23 andreas 3932
        MSG_TRACE("Creating bargraph ...");
59 andreas 3933
        SkBitmap img_bar;
3934
        img_bar.allocPixels(SkImageInfo::MakeN32Premul(sr[1].bm_width, sr[1].bm_height));
3935
        img_bar.eraseColor(SK_ColorTRANSPARENT);
3936
        SkCanvas bar(img_bar, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
23 andreas 3937
 
3938
        for (int ix = 0; ix < sr[1].bm_width; ix++)
3939
        {
3940
            for (int iy = 0; iy < sr[1].bm_height; iy++)
3941
            {
3942
                SkPaint paint;
3943
                SkColor pixel;
3944
 
3945
                if (ix >= startX && ix < width && iy >= startY && iy < height)
3946
                    pixel = image2.getColor(ix, iy);
3947
                else
3948
                    pixel = SK_ColorTRANSPARENT;
3949
 
3950
                paint.setColor(pixel);
59 andreas 3951
                bar.drawPoint(ix, iy, paint);
23 andreas 3952
            }
3953
        }
3954
 
15 andreas 3955
        SkPaint paint;
59 andreas 3956
        paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 3957
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(image1);
3958
        can_bm.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
23 andreas 3959
        paint.setBlendMode(SkBlendMode::kSrcATop);
179 andreas 3960
        _image = SkImage::MakeFromBitmap(img_bar);
3961
        can_bm.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);       // Draw the above created image over the 0% image
142 andreas 3962
    }
3963
    else if (sr[0].bm.empty() && !sr[1].bm.empty())     // Only one bitmap in the second instance
3964
    {
3965
        MSG_TRACE("Drawing second image " << sr[1].bm << " ...");
165 andreas 3966
        SkBitmap image;
167 andreas 3967
        TImgCache::getBitmap(sr[1].bm, &image, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);   // State when level = 100%
142 andreas 3968
        SkCanvas can_bm(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
3969
 
3970
        if (image.empty())
3971
        {
3972
            MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
3973
            TError::setError();
3974
            return false;
3975
        }
3976
 
3977
        int width = sr[1].bm_width;
3978
        int height = sr[1].bm_height;
3979
        int startX = 0;
3980
        int startY = 0;
3981
 
3982
        // Calculation: width / <effective pixels> * level
3983
        // Calculation: height / <effective pixels> * level
3984
        if (dr.compare("horizontal") == 0)
3985
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
3986
        else
3987
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
3988
 
3989
        if (ri && dr.compare("horizontal") == 0)     // range inverted?
3990
        {
3991
            startX = sr[1].mi_width - width;
3992
            width = sr[1].mi_width;
3993
        }
3994
        else if (dr.compare("horizontal") != 0)
3995
        {
3996
            startY = sr[1].mi_height - height;
3997
            height = sr[1].mi_height;
3998
        }
3999
 
4000
        MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
4001
        MSG_TRACE("Creating bargraph ...");
4002
        SkBitmap img_bar;
4003
        img_bar.allocPixels(SkImageInfo::MakeN32Premul(sr[1].bm_width, sr[1].bm_height));
4004
        img_bar.eraseColor(SK_ColorTRANSPARENT);
4005
        SkCanvas bar(img_bar, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4006
        SkPaint pt;
4007
 
4008
        for (int ix = 0; ix < sr[1].bm_width; ix++)
4009
        {
4010
            for (int iy = 0; iy < sr[1].bm_height; iy++)
4011
            {
4012
                SkColor pixel;
4013
 
4014
                if (ix >= startX && ix < width && iy >= startY && iy < height)
4015
                    pixel = image.getColor(ix, iy);
4016
                else
4017
                    pixel = SK_ColorTRANSPARENT;
4018
 
4019
                pt.setColor(pixel);
4020
                bar.drawPoint(ix, iy, pt);
4021
            }
4022
        }
4023
 
4024
        SkPaint paint;
4025
        paint.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 4026
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(img_bar);
4027
        can_bm.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);      // Draw the above created image over the 0% image
15 andreas 4028
    }
4029
    else
4030
    {
38 andreas 4031
        MSG_TRACE("No bitmap defined.");
4032
        int width = wt;
4033
        int height = ht;
15 andreas 4034
        int startX = 0;
4035
        int startY = 0;
4036
 
100 andreas 4037
        // Calculation: width / <effective pixels> * level = <level position>
4038
        // Calculation: height / <effective pixels> * level = <level position>
15 andreas 4039
        if (dr.compare("horizontal") == 0)
4040
            width = (int)((double)width / ((double)rh - (double)rl) * (double)level);
4041
        else
4042
            height = (int)((double)height / ((double)rh - (double)rl) * (double)level);
4043
 
59 andreas 4044
        if (ri && dr.compare("horizontal") == 0)
15 andreas 4045
        {
59 andreas 4046
            startX = wt - width;
38 andreas 4047
            width = wt;
59 andreas 4048
        }
4049
        else if (dr.compare("horizontal") != 0)
4050
        {
4051
            startY = ht - height;
38 andreas 4052
            height = ht;
15 andreas 4053
        }
4054
 
4055
        SkPaint paint;
4056
        paint.setBlendMode(SkBlendMode::kSrc);
4057
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
20 andreas 4058
        paint.setStyle(SkPaint::kFill_Style);
4059
        paint.setAntiAlias(true);
4060
        paint.setStrokeWidth(4);
38 andreas 4061
        paint.setColor(TColor::getSkiaColor(sr[1].cf));
4062
        MSG_DEBUG("Drawing rectangle: X=" << startX << ", Y=" << startY << ", W=" << width << ", H=" << height << ", level=" << level);
15 andreas 4063
        SkRect dst;
4064
        dst.setXYWH(startX, startY, width, height);
20 andreas 4065
        can.drawRect(dst, paint);
99 andreas 4066
        // If we have a slider button defined, we must draw it. To do it, we
4067
        // must look into the system resources to find the credentials to draw
4068
        // the button.
4069
        if (!sd.empty())
4070
        {
4071
            MSG_DEBUG("Attempt to draw the slider button \"" << sd << "\".");
100 andreas 4072
            int innerW = 0;
4073
            int innerH = 0;
4074
 
99 andreas 4075
            SkBitmap slButton = drawSliderButton(sd, TColor::getSkiaColor(sc));
4076
 
4077
            if (slButton.empty())
4078
            {
4079
                MSG_ERROR("Error drawing the slicer button " << sd);
4080
                return true;
4081
            }
4082
 
4083
            double scaleW, scaleH;
100 andreas 4084
            int border_size = getBorderSize(sr[0].bs);
99 andreas 4085
 
4086
            if (dr.compare("horizontal") != 0)
4087
            {
4088
                double scale;
100 andreas 4089
                innerH = (int)((double)(height - border_size * 2 - slButton.info().height() / 2) / ((double)rh - (double)rl) * (double)level) + border_size + slButton.info().height() / 2;
4090
                innerW = width;
4091
                scale = (double)(wt - border_size * 2) / (double)slButton.info().width();
99 andreas 4092
                scaleW = scale;
100 andreas 4093
                scaleH = 1.0;
4094
 
4095
                if (!ri)
4096
                    innerH = height - innerH;
99 andreas 4097
            }
4098
            else
4099
            {
4100
                double scale;
100 andreas 4101
                scale = (double)(ht - border_size * 2) / (double)slButton.info().height();
4102
                scaleW = 1.0;
99 andreas 4103
                scaleH = scale;
100 andreas 4104
                innerW = (int)((double)(width - border_size * 2 - slButton.info().width() / 2) / ((double)rh - (double)rl) * (double)level) + border_size + slButton.info().width() / 2;
4105
                innerH = height;
4106
 
4107
                if (!ri)
4108
                    innerW = width - innerW;
99 andreas 4109
            }
100 andreas 4110
 
99 andreas 4111
            if (scaleImage(&slButton, scaleW, scaleH))
4112
            {
4113
                int w = slButton.info().width();
4114
                int h = slButton.info().height();
4115
 
4116
                if (dr.compare("horizontal") == 0)
4117
                {
100 andreas 4118
                    int pos = innerW;
4119
                    dst.setXYWH(pos - w / 2, border_size, w, h);
99 andreas 4120
                }
4121
                else
4122
                {
100 andreas 4123
                    int pos = innerH;
4124
                    dst.setXYWH(border_size, pos - h / 2, w, h);
99 andreas 4125
                }
4126
 
4127
                SkPaint pnt;
100 andreas 4128
                pnt.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 4129
                sk_sp<SkImage> _image = SkImage::MakeFromBitmap(slButton);
4130
                can.drawImageRect(_image, dst, SkSamplingOptions(), &pnt);
99 andreas 4131
            }
4132
        }
15 andreas 4133
    }
4134
 
4135
    return true;
4136
}
4137
 
99 andreas 4138
SkBitmap TButton::drawSliderButton(const string& slider, SkColor col)
4139
{
4140
    DECL_TRACER("TButton::drawSliderButton(const string& slider)");
4141
 
4142
    SkBitmap slButton;
4143
    // First we look for the slider button.
4144
    if (!gPageManager || !gPageManager->getSystemDraw()->existSlider(slider))
4145
        return slButton;
4146
 
4147
    // There exists one with the wanted name. We grab it and create
4148
    // the images from the files.
4149
    SLIDER_STYLE_t sst;
4150
 
4151
    if (!gPageManager->getSystemDraw()->getSlider(slider, &sst))    // should never be true!
4152
    {
4153
        MSG_ERROR("No slider entry found!");
4154
        return slButton;
4155
    }
4156
 
4157
    int width, height;
4158
 
4159
    if (dr.compare("horizontal") != 0)
4160
    {
100 andreas 4161
        width = (sst.fixedSize / 2) * 2 + sst.fixedSize;
99 andreas 4162
        height = sst.fixedSize;
4163
    }
4164
    else
4165
    {
4166
        width = sst.fixedSize;
100 andreas 4167
        height = (sst.fixedSize / 2) * 2 + sst.fixedSize;
99 andreas 4168
    }
4169
 
4170
    // Retrieve all available slider graphics files from the system
4171
    vector<SLIDER_t> sltList = gPageManager->getSystemDraw()->getSliderFiles(slider);
4172
 
4173
    if (sltList.empty())
4174
    {
4175
        MSG_ERROR("No system slider graphics found!");
4176
        return SkBitmap();
4177
    }
4178
 
4179
    SkPaint paint;
4180
    paint.setBlendMode(SkBlendMode::kSrc);
4181
    slButton.allocN32Pixels(width, height);
4182
    slButton.eraseColor(SK_ColorTRANSPARENT);
4183
    SkCanvas slCan(slButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4184
    vector<SLIDER_t>::iterator sltIter;
4185
    // Loop through list of slider graphic files
4186
    for (sltIter = sltList.begin(); sltIter != sltList.end(); ++sltIter)
4187
    {
4188
        SkBitmap slPart;
4189
        SkBitmap slPartAlpha;
4190
        SkRect dst;
4191
 
4192
        if (dr.compare("horizontal") != 0 && (sltIter->type == SGR_LEFT || sltIter->type == SGR_RIGHT || sltIter->type == SGR_VERTICAL))    // vertical slider
4193
        {
4194
            if (!retrieveImage(sltIter->path, &slPart))     // Get the mask
4195
            {
4196
                MSG_ERROR("Missing slider button mask image " << sltIter->path);
4197
                return SkBitmap();
4198
            }
4199
 
4200
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))   // Get the alpha mask
4201
            {
4202
                MSG_ERROR("Missing slider button alpha image " << sltIter->pathAlpha);
4203
                return SkBitmap();
4204
            }
4205
 
4206
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
4207
 
4208
            if (sl.empty())
4209
                return sl;
4210
 
4211
            switch (sltIter->type)
4212
            {
4213
                case SGR_LEFT:      dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
4214
 
4215
                case SGR_VERTICAL:
100 andreas 4216
                    stretchImageWidth(&sl, sst.fixedSize);
99 andreas 4217
                    dst.setXYWH(sst.fixedSize / 2, 0, sl.info().width(), sl.info().height());
4218
                break;
4219
 
100 andreas 4220
                case SGR_RIGHT:     dst.setXYWH((sst.fixedSize / 2) + sst.fixedSize, 0, sl.info().width(), sl.info().height()); break;
99 andreas 4221
 
4222
                default:
4223
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
4224
            }
4225
 
179 andreas 4226
            sk_sp<SkImage> _image = SkImage::MakeFromBitmap(sl);
4227
            slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
99 andreas 4228
        }
4229
        else if (dr.compare("horizontal") == 0 && (sltIter->type == SGR_TOP || sltIter->type == SGR_BOTTOM || sltIter->type == SGR_HORIZONTAL)) // horizontal slider
4230
        {
4231
            if (!retrieveImage(sltIter->path, &slPart))
4232
            {
4233
                MSG_ERROR("Missing slider button image " << sltIter->path);
4234
                return SkBitmap();
4235
            }
4236
 
4237
            if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))
4238
            {
4239
                MSG_ERROR("Missing slider button image " << sltIter->pathAlpha);
4240
                return SkBitmap();
4241
            }
4242
 
4243
            SkBitmap sl = combineImages(slPart, slPartAlpha, col);
4244
 
4245
            if (sl.empty())
4246
                return sl;
4247
 
4248
            switch (sltIter->type)
4249
            {
4250
                case SGR_TOP:       dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
4251
 
4252
                case SGR_HORIZONTAL:
100 andreas 4253
                    stretchImageHeight(&sl, sst.fixedSize);
99 andreas 4254
                    dst.setXYWH(0, sst.fixedSize / 2, sl.info().width(), sl.info().height());
4255
                break;
4256
 
100 andreas 4257
                case SGR_BOTTOM:    dst.setXYWH(0, (sst.fixedSize / 2) + sst.fixedSize, sl.info().width(), sl.info().height()); break;
99 andreas 4258
 
4259
                default:
4260
                    MSG_WARNING("Invalid type " << sltIter->type << " found!");
4261
            }
4262
 
179 andreas 4263
            sk_sp<SkImage> _image = SkImage::MakeFromBitmap(sl);
4264
            slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
99 andreas 4265
        }
4266
    }
4267
 
4268
    return slButton;
4269
}
4270
 
8 andreas 4271
bool TButton::buttonIcon(SkBitmap* bm, int instance)
4272
{
4273
    DECL_TRACER("TButton::buttonIcon(SkBitmap* bm, int instance)");
4274
 
165 andreas 4275
    if (instance < 0 || (size_t)instance >= sr.size())
4276
    {
4277
        MSG_ERROR("Invalid instance " << instance);
4278
        return false;
4279
    }
4280
 
8 andreas 4281
    if (sr[instance].ii <= 0)
4282
    {
4283
        MSG_TRACE("No icon defined!");
4284
        return true;
7 andreas 4285
    }
4286
 
8 andreas 4287
    MSG_DEBUG("Drawing an icon ...");
4288
 
4289
    if (!gIcons)
4290
    {
192 andreas 4291
        MSG_WARNING("No icons were defined!");
4292
        return true;
4293
    }
8 andreas 4294
 
192 andreas 4295
    string file = gIcons->getFile(sr[instance].ii);
4296
 
4297
    if (file.empty())
4298
    {
4299
        MSG_WARNING("The icon " << sr[instance].ii << " was not found in table!");
4300
        return true;
8 andreas 4301
    }
4302
 
4303
    MSG_DEBUG("Loading icon file " << file);
4304
    sk_sp<SkData> image;
4305
    SkBitmap icon;
4306
 
4307
    if (!(image = readImage(file)))
192 andreas 4308
        return true;
8 andreas 4309
 
4310
    DecodeDataToBitmap(image, &icon);
4311
 
4312
    if (icon.empty())
4313
    {
4314
        MSG_WARNING("Could not create an icon for element " << sr[instance].ii << " on button " << bi << " (" << na << ")");
192 andreas 4315
        return true;
8 andreas 4316
    }
4317
 
4318
    SkImageInfo info = icon.info();
4319
    POSITION_t position = calcImagePosition(icon.width(), icon.height(), SC_ICON, instance);
4320
 
4321
    if (!position.valid)
4322
    {
4323
        MSG_ERROR("Error calculating the position of the image for button number " << bi);
4324
        TError::setError();
4325
        return false;
4326
    }
4327
 
10 andreas 4328
    MSG_DEBUG("Putting Icon on top of bitmap ...");
4329
    SkPaint paint;
4330
    paint.setBlendMode(SkBlendMode::kSrcOver);
4331
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
8 andreas 4332
 
17 andreas 4333
    if (position.overflow)
4334
    {
179 andreas 4335
        SkRect irect;
17 andreas 4336
        SkRect bdst;
4337
        SkBitmap dst;
4338
        int left = (position.left >= 0) ? 0 : position.left * -1;
4339
        int top = (position.top >= 0) ? 0 : position.top * -1;
4340
        int width = std::min(wt, info.width());
4341
        int height = std::min(ht, info.height());
4342
        irect.setXYWH(left, top, width, height);
4343
        bm->getBounds(&bdst);
179 andreas 4344
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(icon);
4345
        can.drawImageRect(_image, irect, bdst, SkSamplingOptions(), &paint, SkCanvas::kStrict_SrcRectConstraint);
17 andreas 4346
    }
4347
    else
179 andreas 4348
    {
4349
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(icon);
4350
        can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
4351
    }
17 andreas 4352
 
8 andreas 4353
    return true;
4354
}
4355
 
38 andreas 4356
bool TButton::buttonText(SkBitmap* bm, int inst)
8 andreas 4357
{
38 andreas 4358
    DECL_TRACER("TButton::buttonText(SkBitmap* bm, int inst)");
8 andreas 4359
 
38 andreas 4360
    int instance = inst;
4361
 
4362
    if ((size_t)instance >= sr.size())
4363
        instance = sr.size() - 1;
4364
    else if (instance < 0)
4365
        instance = 0;
4366
 
67 andreas 4367
    if (sr[instance].te.empty() || !mFonts)     // Is there a text and fonts?
4368
    {                                           // No, then return
4369
        MSG_DEBUG("Empty text string.");
4370
        return true;
4371
    }
4372
 
4373
    MSG_DEBUG("Searching for font number " << sr[instance].fi << " with text " << sr[instance].te);
4374
    FONT_T font = mFonts->getFont(sr[instance].fi);
4375
 
161 andreas 4376
    if (font.file.empty())
7 andreas 4377
    {
161 andreas 4378
        MSG_WARNING("No font file name found for font " << sr[instance].fi);
4379
        return true;
4380
    }
7 andreas 4381
 
161 andreas 4382
    sk_sp<SkTypeface> typeFace = mFonts->getTypeFace(sr[instance].fi);
4383
    SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4384
 
4385
    if (!typeFace)
4386
    {
164 andreas 4387
        MSG_WARNING("Error creating type face " << font.fullName);
161 andreas 4388
    }
4389
 
4390
    SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
164 andreas 4391
    SkFont skFont;
4392
 
4393
    if (typeFace && typeFace->countTables() > 0)
4394
        skFont.setTypeface(typeFace);
4395
 
4396
    skFont.setSize(fontSizePt);
161 andreas 4397
    skFont.setEdging(SkFont::Edging::kAntiAlias);
4398
    MSG_DEBUG("Wanted font size: " << font.size << ", this is " << fontSizePt << " pt");
4399
 
4400
    SkPaint paint;
4401
    paint.setAntiAlias(true);
4402
    paint.setColor(TColor::getSkiaColor(sr[instance].ct));
4403
    paint.setStyle(SkPaint::kFill_Style);
4404
 
4405
    SkFontMetrics metrics;
4406
    skFont.getMetrics(&metrics);
4407
    int lines = numberLines(sr[instance].te);
164 andreas 4408
//    MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
4409
//    MSG_DEBUG("fCapHeight:    " << metrics.fCapHeight);
4410
//    MSG_DEBUG("fAscent:       " << metrics.fAscent);
4411
//    MSG_DEBUG("fDescent:      " << metrics.fDescent);
4412
//    MSG_DEBUG("fLeading:      " << metrics.fLeading);
4413
//    MSG_DEBUG("fXHeight:      " << metrics.fXHeight);
161 andreas 4414
 
4415
    MSG_DEBUG("Found " << lines << " lines.");
4416
 
4417
    if (lines > 1 || sr[instance].ww)
4418
    {
4419
        vector<string> textLines;
4420
 
4421
        if (!sr[instance].ww)
4422
            textLines = splitLine(sr[instance].te);
4423
        else
6 andreas 4424
        {
161 andreas 4425
            textLines = splitLine(sr[instance].te, wt, ht, skFont, paint);
4426
            lines = textLines.size();
67 andreas 4427
        }
7 andreas 4428
 
161 andreas 4429
        MSG_DEBUG("Calculated number of lines: " << lines);
4430
        int lineHeight = (metrics.fAscent * -1) + metrics.fDescent;
4431
        int totalHeight = lineHeight * lines;
19 andreas 4432
 
161 andreas 4433
        if (totalHeight > ht)
4434
        {
4435
            lines = ht / lineHeight;
4436
            totalHeight = lineHeight * lines;
4437
        }
8 andreas 4438
 
161 andreas 4439
        MSG_DEBUG("Line height: " << lineHeight << ", total height: " << totalHeight);
4440
        vector<string>::iterator iter;
4441
        int line = 0;
4442
        int maxWidth = 0;
69 andreas 4443
 
161 andreas 4444
        if (textLines.size() > 0)
67 andreas 4445
        {
161 andreas 4446
            // Calculate the maximum width
4447
            for (iter = textLines.begin(); iter != textLines.end(); ++iter)
4448
            {
4449
                SkRect rect;
4450
                skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
67 andreas 4451
 
161 andreas 4452
                if (rect.width() > maxWidth)
4453
                    maxWidth = rect.width();
67 andreas 4454
            }
8 andreas 4455
 
161 andreas 4456
            POSITION_t pos = calcImagePosition(maxWidth, totalHeight, SC_TEXT, instance);
8 andreas 4457
 
161 andreas 4458
            if (!pos.valid)
67 andreas 4459
            {
161 andreas 4460
                MSG_ERROR("Error calculating the text position!");
4461
                TError::setError();
4462
                return false;
67 andreas 4463
            }
40 andreas 4464
 
161 andreas 4465
            SkScalar lnHt = metrics.fAscent * -1;
96 andreas 4466
 
161 andreas 4467
            for (iter = textLines.begin(); iter != textLines.end(); ++iter)
69 andreas 4468
            {
161 andreas 4469
                sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
4470
                MSG_DEBUG("Trying to print line: " << *iter);
4471
                // We want to take care about the horizontal position.
4472
                SkRect rect;
4473
                skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
4474
                SkScalar horizontal = 0.0;
4475
 
4476
                switch(sr[instance].jt)
96 andreas 4477
                {
161 andreas 4478
                    case ORI_BOTTOM_MIDDLE:
4479
                    case ORI_CENTER_MIDDLE:
4480
                    case ORI_TOP_MIDDLE:
4481
                        horizontal = (wt - rect.width()) / 2.0f;
4482
                    break;
40 andreas 4483
 
161 andreas 4484
                    case ORI_BOTTOM_RIGHT:
4485
                    case ORI_CENTER_RIGHT:
4486
                    case ORI_TOP_RIGHT:
4487
                        horizontal = wt - rect.width();
4488
                    break;
69 andreas 4489
 
161 andreas 4490
                    default:
4491
                        horizontal = pos.left;
96 andreas 4492
                }
40 andreas 4493
 
161 andreas 4494
                SkScalar startX = horizontal;
4495
                SkScalar startY = (SkScalar)pos.top + (SkScalar)lineHeight * (SkScalar)line;
4496
                MSG_DEBUG("x=" << startX << ", y=" << startY);
4497
                bool tEffect = false;
4498
                // Text effects
4499
                if (sr[instance].et > 0)
4500
                    tEffect = textEffect(&canvas, blob, startX, startY + lnHt, instance);
67 andreas 4501
 
161 andreas 4502
                if (!tEffect)
4503
                    canvas.drawTextBlob(blob.get(), startX, startY + lnHt, paint);
69 andreas 4504
 
161 andreas 4505
                line++;
69 andreas 4506
 
161 andreas 4507
                if (line > lines)
4508
                    break;
67 andreas 4509
            }
4510
        }
161 andreas 4511
    }
4512
    else    // single line
4513
    {
4514
        string text = sr[instance].te;
4515
        sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.data(), skFont);
4516
        SkRect rect;
4517
        skFont.measureText(text.data(), text.size(), SkTextEncoding::kUTF8, &rect, &paint);
164 andreas 4518
        MSG_DEBUG("Calculated Skia rectangle of font: width=" << rect.width() << ", height=" << rect.height());
4519
        POSITION_t position;
66 andreas 4520
 
164 andreas 4521
        if (metrics.fCapHeight >= 1.0)
4522
            position = calcImagePosition(rect.width(), metrics.fCapHeight, SC_TEXT, instance);
4523
        else
4524
            position = calcImagePosition(rect.width(), rect.height(), SC_TEXT, instance);
4525
 
161 andreas 4526
        if (!position.valid)
4527
        {
4528
            MSG_ERROR("Error calculating the text position!");
4529
            TError::setError();
4530
            return false;
4531
        }
66 andreas 4532
 
161 andreas 4533
        MSG_DEBUG("Printing line " << text);
4534
        SkScalar startX = (SkScalar)position.left;
164 andreas 4535
        SkScalar startY = (SkScalar)position.top;
10 andreas 4536
 
164 andreas 4537
        if (metrics.fCapHeight >= 1.0)
4538
            startY += metrics.fCapHeight;   // This is the offset of the line
4539
        else
4540
            startY += rect.height();        // This is the offset of the line
4541
 
161 andreas 4542
        FONT_TYPE sym = TFont::isSymbol(typeFace);
4543
        bool tEffect = false;
4544
        // Text effects
4545
        if (sr[instance].et > 0)
164 andreas 4546
            tEffect = textEffect(&canvas, blob, startX, startY, instance);
67 andreas 4547
 
161 andreas 4548
        if (!tEffect && utf8Strlen(text) > 1)
164 andreas 4549
            canvas.drawTextBlob(blob.get(), startX, startY, paint);
161 andreas 4550
        else
4551
        {
4552
            int count = 0;
4553
            uint16_t *glyphs = nullptr;
4554
 
4555
            if (sym == FT_SYM_MS)
8 andreas 4556
            {
161 andreas 4557
                MSG_DEBUG("Microsoft proprietary symbol font detected.");
4558
                uint16_t *uni;
4559
                size_t num = TFont::utf8ToUtf16(text, &uni, true);
4560
                MSG_DEBUG("Got " << num << " unichars, first unichar: " << std::hex << std::setw(4) << std::setfill('0') << *uni << std::dec);
7 andreas 4561
 
161 andreas 4562
                if (num > 0)
8 andreas 4563
                {
161 andreas 4564
                    glyphs = new uint16_t[num];
4565
                    size_t glyphSize = sizeof(uint16_t) * num;
4566
                    count = skFont.textToGlyphs(uni, num, SkTextEncoding::kUTF16, glyphs, glyphSize);
8 andreas 4567
 
161 andreas 4568
                    if (count <= 0)
67 andreas 4569
                    {
161 andreas 4570
                        delete[] glyphs;
4571
                        glyphs = TFont::textToGlyphs(text, typeFace, &num);
4572
                        count = num;
67 andreas 4573
                    }
4574
                }
4575
                else
4576
                {
161 andreas 4577
                    canvas.drawTextBlob(blob.get(), startX, startY, paint);
4578
                    return true;
67 andreas 4579
                }
4580
 
161 andreas 4581
                if (uni)
4582
                    delete[] uni;
4583
            }
4584
            else if (tEffect)
4585
                return true;
4586
            else
4587
            {
4588
                glyphs = new uint16_t[text.size()];
4589
                size_t glyphSize = sizeof(uint16_t) * text.size();
4590
                count = skFont.textToGlyphs(text.data(), text.size(), SkTextEncoding::kUTF8, glyphs, glyphSize);
4591
            }
156 andreas 4592
 
161 andreas 4593
            if (glyphs)
4594
            {
4595
                MSG_DEBUG("1st glyph: 0x" << std::hex << std::setw(8) << std::setfill('0') << *glyphs << ", # glyphs: " << std::dec << count);
164 andreas 4596
                canvas.drawSimpleText(glyphs, sizeof(uint16_t) * count, SkTextEncoding::kGlyphID, startX, startY, skFont, paint);
8 andreas 4597
            }
161 andreas 4598
            else    // Try to print something
4599
            {
4600
                MSG_WARNING("Got no glyphs! Try to print: " << text);
4601
                glyphs = new uint16_t[text.size()];
4602
                size_t glyphSize = sizeof(uint16_t) * text.size();
4603
                count = skFont.textToGlyphs(text.data(), text.size(), SkTextEncoding::kUTF8, glyphs, glyphSize);
4604
                canvas.drawSimpleText(glyphs, sizeof(uint16_t) * count, SkTextEncoding::kGlyphID, startX, startY, skFont, paint);
4605
            }
4606
 
4607
            delete[] glyphs;
8 andreas 4608
        }
4609
    }
4610
 
4611
    return true;
4612
}
4613
 
69 andreas 4614
int TButton::calcLineHeight(const string& text, SkFont& font)
40 andreas 4615
{
69 andreas 4616
    DECL_TRACER("TButton::calcLineHeight(const string& text, SkFont& font)");
40 andreas 4617
 
69 andreas 4618
    size_t pos = text.find("\n");       // Search for a line break.
4619
    string lText = text;
4620
 
4621
    if (pos != string::npos)            // Do we have found a line break?
4622
        lText = text.substr(0, pos - 1);// Yes, take only the text up to 1 before the line break (only 1 line).
4623
 
4624
    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(lText.c_str(), font);
40 andreas 4625
    SkRect rect = blob.get()->bounds();
4626
    return rect.height();
4627
}
4628
 
66 andreas 4629
bool TButton::textEffect(SkCanvas *canvas, sk_sp<SkTextBlob>& blob, SkScalar startX, SkScalar startY, int instance)
4630
{
4631
    DECL_TRACER("TButton::textEffect(SkBitmap *bm, int instance)");
4632
 
4633
    if (!canvas)
4634
        return false;
4635
 
165 andreas 4636
    if (instance < 0 || (size_t)instance >= sr.size())
4637
    {
4638
        MSG_ERROR("Invalid instance " << instance);
4639
        return false;
4640
    }
4641
 
66 andreas 4642
    // Drop Shadow
4643
    if (sr[instance].et >= 9 && sr[instance].et <= 32)
4644
    {
4645
        SkScalar gap = 0.0;
4646
        SkScalar sigma = 0.0;
4647
        SkScalar xDrop = 0.0;
4648
        SkScalar yDrop = 0.0;
4649
        uint8_t blurAlpha = 255;
4650
        SkPaint paint;
4651
        paint.setAntiAlias(true);
4652
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
4653
 
4654
        // Soft drop shadow
4655
        if (sr[instance].et >= 9 && sr[instance].et <= 16)
4656
        {
4657
            gap = (SkScalar)sr[instance].et - 8.0f;
4658
            sigma = 3.0f;
4659
            blurAlpha = 127;
4660
        }
4661
        else if (sr[instance].et >= 17 && sr[instance].et <= 24) // Medium drop shadow
4662
        {
4663
            gap = (SkScalar)sr[instance].et - 16.0f;
4664
            sigma = 2.0f;
4665
            blurAlpha = 159;
4666
        }
4667
        else    // Hard drop shadow
4668
        {
4669
            gap = (SkScalar)sr[instance].et - 24.0f;
4670
            sigma = 1.1f;
4671
            blurAlpha = 207;
4672
        }
4673
 
4674
        xDrop = gap;
4675
        yDrop = gap;
4676
        SkPaint blur(paint);
4677
        blur.setAlpha(blurAlpha);
4678
        blur.setColor(TColor::getSkiaColor(sr[instance].ec));
4679
        blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, 0));
4680
        canvas->drawTextBlob(blob.get(), startX + xDrop, startY + yDrop, blur);
4681
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
4682
        return true;
4683
    }
161 andreas 4684
    else if (sr[instance].et >= 5 && sr[instance].et <= 8)  // Glow
4685
    {
4686
        SkScalar sigma = 0.0;
66 andreas 4687
 
161 andreas 4688
        switch(sr[instance].et)
4689
        {
4690
            case 5: sigma = 2.0; break;     // Glow-S
4691
            case 6: sigma = 4.0; break;     // Glow-M
4692
            case 7: sigma = 6.0; break;     // Glow-L
4693
            case 8: sigma = 8.0; break;     // Glow-X
4694
        }
4695
 
4696
        SkPaint paint, blur;
4697
        paint.setAntiAlias(true);
4698
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
4699
        blur.setColor(TColor::getSkiaColor(sr[instance].ec));
4700
        blur.setStyle(SkPaint::kStroke_Style);
4701
        blur.setStrokeWidth(sigma / 1.5);
4702
        blur.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, sigma));
4703
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
4704
        canvas->drawTextBlob(blob.get(), startX, startY, blur);
4705
        return true;
4706
    }
4707
    else if (sr[instance].et >= 1 && sr[instance].et <= 4)  // Outline
4708
    {
4709
        SkScalar sigma = 0.0;
4710
 
4711
        switch(sr[instance].et)
4712
        {
4713
            case 1: sigma = 1.0; break;     // Outline-S
4714
            case 2: sigma = 2.0; break;     // Outline-M
4715
            case 3: sigma = 4.0; break;     // Outline-L
4716
            case 4: sigma = 6.0; break;     // Outline-X
4717
        }
4718
 
4719
        SkPaint paint, outline;
4720
        paint.setAntiAlias(true);
4721
        paint.setColor(TColor::getSkiaColor(sr[instance].ct));
4722
        outline.setAntiAlias(true);
4723
        outline.setColor(TColor::getSkiaColor(sr[instance].ec));
4724
        outline.setStyle(SkPaint::kStroke_Style);
4725
        outline.setStrokeWidth(sigma);
4726
        canvas->drawTextBlob(blob.get(), startX, startY, outline);
4727
        canvas->drawTextBlob(blob.get(), startX, startY, paint);
4728
        return true;
4729
    }
4730
 
66 andreas 4731
    return false;
4732
}
4733
 
159 andreas 4734
bool TButton::buttonBorder(SkBitmap* bm, int inst)
8 andreas 4735
{
4736
    DECL_TRACER("TButton::buttonBorder(SkBitmap* bm, int instance)");
4737
 
159 andreas 4738
    int instance = inst;
4739
 
4740
    if (instance < 0)
4741
        instance = 0;
4742
    else if ((size_t)instance > sr.size())
4743
        instance = (int)sr.size() - 1;
4744
 
8 andreas 4745
    if (sr[instance].bs.empty())
4746
    {
4747
        MSG_DEBUG("No border defined.");
4748
        return true;
4749
    }
4750
 
79 andreas 4751
    // Try to find the border in the system table
160 andreas 4752
    BORDER_t bd, bda;
81 andreas 4753
    int borderIndex = -1;
4754
    int i = 0;
79 andreas 4755
 
81 andreas 4756
    while (sysBorders[i].id)
79 andreas 4757
    {
101 andreas 4758
        string sysBrd = sysBorders[i].name;
4759
 
4760
        if (sysBrd == sr[instance].bs)
81 andreas 4761
        {
4762
            borderIndex = i;
168 andreas 4763
            MSG_DEBUG("Found internal system border [" << i << "]: " << sysBrd);
81 andreas 4764
            break;
4765
        }
4766
 
4767
        i++;
4768
    }
4769
 
4770
    bool classExist = (gPageManager && gPageManager->getSystemDraw());
4771
 
4772
    if ((classExist && borderIndex >= 0 && !sysBorders[borderIndex].calc) || (classExist && borderIndex < 0))
4773
    {
168 andreas 4774
        int numBorders = 0;
169 andreas 4775
        bool extBorder = false;
4776
 
168 andreas 4777
        string borderName1 = (bs.empty() ? sr[instance].bs : bs);
4778
        string borderName2 = (!bs.empty() && !sr[instance].bs.empty() ? sr[instance].bs : bs);
79 andreas 4779
 
169 andreas 4780
        if (bs.empty())
4781
        {
4782
            if (gPageManager->getSystemDraw()->getBorder(sr[instance].bs, TSystemDraw::LT_OFF, &bd))
4783
                numBorders++;
79 andreas 4784
 
169 andreas 4785
            if (gPageManager->getSystemDraw()->getBorder(sr[instance].bs, TSystemDraw::LT_ON, &bda))
4786
                numBorders++;
4787
 
4788
            if (numBorders == 2)
4789
                extBorder = true;
4790
        }
4791
        else if (!sr[instance].bs.empty())
168 andreas 4792
        {
169 andreas 4793
            if (gPageManager->getSystemDraw()->getBorder(bs, (instance == 0 ? TSystemDraw::LT_OFF : TSystemDraw::LT_ON), &bd, sr[instance].bs))
4794
            {
4795
                numBorders++;
4796
                extBorder = true;
4797
            }
4798
        }
4799
 
4800
        if (extBorder)
4801
        {
168 andreas 4802
            MSG_DEBUG("System border \"" << borderName1 << "\" and \"" << borderName2 << "\" found.");
4803
            SkColor color = TColor::getSkiaColor(sr[instance].cb);      // border color
4804
            SkColor bgColor = TColor::getSkiaColor(sr[instance].cf);    // fill color
4805
            MSG_DEBUG("Button color: #" << std::setw(6) << std::setfill('0') << std::hex << color);
4806
            // Load images
4807
            SkBitmap imgB, imgBR, imgR, imgTR, imgT, imgTL, imgL, imgBL;
79 andreas 4808
 
168 andreas 4809
            imgB = retrieveBorderImage(bd.b, bda.b, color, bgColor);
79 andreas 4810
 
168 andreas 4811
            if (imgB.empty())
4812
                return false;
160 andreas 4813
 
168 andreas 4814
            MSG_DEBUG("Got images " << bd.b << " and " << bda.b << " with size " << imgB.info().width() << " x " << imgB.info().height());
4815
            imgBR = retrieveBorderImage(bd.br, bda.br, color, bgColor);
160 andreas 4816
 
168 andreas 4817
            if (imgBR.empty())
4818
                return false;
79 andreas 4819
 
168 andreas 4820
            MSG_DEBUG("Got images " << bd.br << " and " << bda.br << " with size " << imgBR.info().width() << " x " << imgBR.info().height());
4821
            imgR = retrieveBorderImage(bd.r, bda.r, color, bgColor);
79 andreas 4822
 
168 andreas 4823
            if (imgR.empty())
4824
                return false;
79 andreas 4825
 
168 andreas 4826
            MSG_DEBUG("Got images " << bd.r << " and " << bda.r << " with size " << imgR.info().width() << " x " << imgR.info().height());
4827
            imgTR = retrieveBorderImage(bd.tr, bda.tr, color, bgColor);
79 andreas 4828
 
168 andreas 4829
            if (imgTR.empty())
4830
                return false;
79 andreas 4831
 
168 andreas 4832
            MSG_DEBUG("Got images " << bd.tr << " and " << bda.tr << " with size " << imgTR.info().width() << " x " << imgTR.info().height());
4833
            imgT = retrieveBorderImage(bd.t, bda.t, color, bgColor);
79 andreas 4834
 
168 andreas 4835
            if (imgT.empty())
4836
                return false;
79 andreas 4837
 
168 andreas 4838
            MSG_DEBUG("Got images " << bd.t << " and " << bda.t << " with size " << imgT.info().width() << " x " << imgT.info().height());
4839
            imgTL = retrieveBorderImage(bd.tl, bda.tl, color, bgColor);
79 andreas 4840
 
168 andreas 4841
            if (imgTL.empty())
4842
                return false;
79 andreas 4843
 
168 andreas 4844
            MSG_DEBUG("Got images " << bd.tl << " and " << bda.tl << " with size " << imgTL.info().width() << " x " << imgTL.info().height());
4845
            imgL = retrieveBorderImage(bd.l, bda.l, color, bgColor);
79 andreas 4846
 
168 andreas 4847
            if (imgL.empty())
4848
                return false;
79 andreas 4849
 
192 andreas 4850
            mBorderWidth = imgL.info().width();
168 andreas 4851
            MSG_DEBUG("Got images " << bd.l << " and " << bda.l << " with size " << imgL.info().width() << " x " << imgL.info().height());
4852
            imgBL = retrieveBorderImage(bd.bl, bda.bl, color, bgColor);
79 andreas 4853
 
168 andreas 4854
            if (imgBL.empty())
4855
                return false;
79 andreas 4856
 
168 andreas 4857
            MSG_DEBUG("Got images " << bd.bl << " and " << bda.bl << " with size " << imgBL.info().width() << " x " << imgBL.info().height());
169 andreas 4858
            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 4859
            MSG_DEBUG("Total size: " << wt << " x " << ht);
4860
            stretchImageWidth(&imgB, wt - imgBL.info().width() - imgBR.info().width());
4861
            stretchImageWidth(&imgT, wt - imgTL.info().width() - imgTR.info().width());
4862
            stretchImageHeight(&imgL, ht - imgTL.info().height() - imgBL.info().height());
4863
            stretchImageHeight(&imgR, ht - imgTR.info().height() - imgBR.info().height());
169 andreas 4864
            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 4865
            // Draw the frame
4866
            SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4867
            SkPaint paint;
79 andreas 4868
 
168 andreas 4869
            paint.setBlendMode(SkBlendMode::kSrcOver);
179 andreas 4870
            sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgB);
4871
            canvas.drawImage(_image, imgBL.info().width(), ht - imgB.info().height(), SkSamplingOptions(), &paint);
4872
            _image = SkImage::MakeFromBitmap(imgBR);
4873
            canvas.drawImage(_image, wt - imgBR.info().width(), ht - imgBR.info().height(), SkSamplingOptions(), &paint);
4874
            _image = SkImage::MakeFromBitmap(imgR);
4875
            canvas.drawImage(_image, wt - imgR.info().width(), imgTR.info().height(), SkSamplingOptions(), &paint);
4876
            _image = SkImage::MakeFromBitmap(imgTR);
4877
            canvas.drawImage(_image, wt - imgTR.info().width(), 0, SkSamplingOptions(), &paint);
4878
            _image = SkImage::MakeFromBitmap(imgT);
4879
            canvas.drawImage(_image, imgTL.info().width(), 0, SkSamplingOptions(), &paint);
4880
            _image = SkImage::MakeFromBitmap(imgTL);
4881
            canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
4882
            _image = SkImage::MakeFromBitmap(imgL);
4883
            canvas.drawImage(_image, 0, imgTL.info().height(), SkSamplingOptions(), &paint);
4884
            _image = SkImage::MakeFromBitmap(imgBL);
4885
            canvas.drawImage(_image, 0, ht - imgBL.info().height(), SkSamplingOptions(), &paint);
168 andreas 4886
            return true;
4887
        }
79 andreas 4888
    }
4889
 
81 andreas 4890
    // Here we've not found the wanted border in the system table. Reasons may
4891
    // be a wrong name or the system images don't exist.
4892
    // We'll try to find it in our internal table. This is a fallback which
4893
    // provides only a limited number of system borders.
8 andreas 4894
 
81 andreas 4895
    if (borderIndex < 0)
4896
        return true;
4897
 
4898
    bool systemBrdExist = false;
4899
 
4900
    if (classExist && gPageManager->getSystemDraw()->getBorder(sr[instance].bs, ((instance == 0) ? TSystemDraw::LT_OFF : TSystemDraw::LT_ON), &bd))
4901
        systemBrdExist = true;
4902
 
4903
    if (sr[instance].bs.compare(sysBorders[borderIndex].name) == 0)
8 andreas 4904
    {
81 andreas 4905
        MSG_DEBUG("Border " << sysBorders[borderIndex].name << " found.");
4906
        SkCanvas canvas(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
4907
        SkPaint paint;
4908
        SkColor color = TColor::getSkiaColor(sr[instance].cb);
7 andreas 4909
 
81 andreas 4910
        paint.setColor(color);
4911
        paint.setBlendMode(SkBlendMode::kSrc);
4912
        paint.setStyle(SkPaint::kStroke_Style);
4913
        SkRRect outher, inner;
4914
        SkScalar radius = (SkScalar)sysBorders[borderIndex].radius;
4915
        int red, green, blue;
4916
        SkColor borderColor, bcLight, bcDark;
4917
        int lineWidth = 0;
8 andreas 4918
 
81 andreas 4919
        switch (sysBorders[borderIndex].id)
4920
        {
4921
            case 1: // Single Frame
4922
            case 2: // Double Frame
4923
            case 3: // Quad Frame
192 andreas 4924
                mBorderWidth = sysBorders[borderIndex].width;
81 andreas 4925
                paint.setStrokeWidth(sysBorders[borderIndex].width);
4926
                canvas.drawRect(calcRect(wt, ht, sysBorders[borderIndex].width), paint);
4927
            break;
8 andreas 4928
 
81 andreas 4929
            case 4: // Picture Frame
4930
                {
192 andreas 4931
                    mBorderWidth = 2;
81 andreas 4932
                    paint.setStrokeWidth(2);
4933
                    SkRect rect = SkRect::MakeXYWH(0, 0, wt, ht);
4934
                    canvas.drawRect(rect, paint);
4935
                    rect = SkRect::MakeXYWH(4, 4, wt - 4, ht - 4);
4936
                    canvas.drawRect(rect, paint);
4937
                }
4938
            break;
8 andreas 4939
 
81 andreas 4940
            case 5: // Circle 15
4941
            case 6: // Circle 25
4942
            case 7: // Circle 35
4943
            case 8: // Circle 45
4944
            case 9: // Circle 55
4945
            case 10: // Circle 65
4946
            case 11: // Circle 75
4947
            case 12: // Circle 85
4948
            case 13: // Circle 95
4949
            case 14: // Circle 105
4950
            case 15: // Circle 115
4951
            case 16: // Circle 125
4952
            case 17: // Circle 135
4953
            case 18: // Circle 145
4954
            case 19: // Circle 155
4955
            case 20: // Circle 165
4956
            case 21: // Circle 175
4957
            case 22: // Circle 185
4958
            case 23: // Circle 195
4959
                if (systemBrdExist)
4960
                {
4961
                    radius = (double)bd.border.idealWidth / 2.0;    // Take diameter and calculate radius
99 andreas 4962
                    lineWidth = bd.border.textLeft;
81 andreas 4963
                }
4964
                else
4965
                    lineWidth = sysBorders[borderIndex].width;
40 andreas 4966
 
192 andreas 4967
                mBorderWidth = lineWidth;
101 andreas 4968
                paint.setStrokeWidth(1.0);
4969
                paint.setStyle(SkPaint::kFill_Style);
4970
                MSG_DEBUG("Line width: " << lineWidth << ", radius: " << radius);
4971
                // We draw a rounded rectangle to "clip" the corners. To do this
4972
                // in a way to not miss any pixel, we draw a rectangle followed
4973
                // by a rounded rectangle as an inner one. The space between
4974
                // them will be filled transparent.
81 andreas 4975
                outher = SkRRect::MakeRect({0, 0, (SkScalar)wt, (SkScalar)ht});
101 andreas 4976
                inner = SkRRect::MakeRectXY(calcRect(wt, ht, 1), radius, radius);
81 andreas 4977
                paint.setColor(SK_ColorTRANSPARENT);
4978
                canvas.drawDRRect(outher, inner, paint);
101 andreas 4979
                // Here we draw the rounded rectangle.
4980
                paint.setStyle(SkPaint::kStroke_Style);
4981
                paint.setStrokeWidth(lineWidth);
4982
                paint.setColor(color);
4983
                paint.setStrokeJoin(SkPaint::kRound_Join);
4984
                canvas.drawRoundRect(calcRect(wt, ht, lineWidth), radius, radius, paint);
4985
 
81 andreas 4986
            break;
40 andreas 4987
 
81 andreas 4988
            case 24:    // AMX Elite Inset -L
4989
            case 26:    // AMX Elite Inset -M
4990
            case 28:    // AMX Elite Inset -S
40 andreas 4991
                {
192 andreas 4992
                    mBorderWidth = sysBorders[borderIndex].width;
40 andreas 4993
                    borderColor = TColor::getSkiaColor(sr[instance].cb);
81 andreas 4994
                    vector<SkColor> cols = TColor::colorRange(borderColor, sysBorders[borderIndex].width, 40, TColor::DIR_LIGHT_DARK_LIGHT);
40 andreas 4995
                    vector<SkColor>::iterator iter;
4996
                    int i = 0;
4997
 
4998
                    for (iter = cols.begin(); iter != cols.end(); ++iter)
4999
                    {
5000
                        paint.setStrokeWidth(1);
5001
                        paint.setColor(*iter);
5002
                        SkRect rect = SkRect::MakeXYWH(i, i, wt - i, ht - i);
5003
                        canvas.drawRect(rect, paint);
5004
                        i++;
5005
                    }
5006
                }
81 andreas 5007
            break;
40 andreas 5008
 
81 andreas 5009
            case 25:    // AMX Elite Raised -L
5010
            case 27:    // AMX Elite Raised -M
5011
            case 29:    // AMX Elite Raised -S
5012
            {
192 andreas 5013
                mBorderWidth = sysBorders[borderIndex].width;
81 andreas 5014
                borderColor = TColor::getSkiaColor(sr[instance].cb);
5015
                vector<SkColor> cols = TColor::colorRange(borderColor, sysBorders[borderIndex].width, 40, TColor::DIR_DARK_LIGHT_DARK);
5016
                vector<SkColor>::iterator iter;
5017
                int i = 0;
5018
 
5019
                for (iter = cols.begin(); iter != cols.end(); ++iter)
5020
                {
40 andreas 5021
                    paint.setStrokeWidth(1);
81 andreas 5022
                    paint.setColor(*iter);
5023
                    SkRect rect = SkRect::MakeXYWH(i, i, wt - i, ht - i);
5024
                    canvas.drawRect(rect, paint);
5025
                    i++;
5026
                }
5027
            }
5028
            break;
40 andreas 5029
 
81 andreas 5030
            case 30:    // BevelInset -L
5031
            case 32:    // Bevel Inset -M
5032
            case 34:    // Bevel Inset -S
192 andreas 5033
                mBorderWidth = sysBorders[borderIndex].width;
81 andreas 5034
                borderColor = TColor::getSkiaColor(sr[instance].cb);
5035
                red = std::min((int)SkColorGetR(borderColor) + 20, 255);
5036
                green = std::min((int)SkColorGetG(borderColor) + 20, 255);
5037
                blue = std::min((int)SkColorGetB(borderColor) + 20, 255);
5038
                bcLight = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
5039
                red = std::max((int)SkColorGetR(borderColor) - 20, 0);
5040
                green = std::max((int)SkColorGetG(borderColor) - 20, 0);
5041
                blue = std::max((int)SkColorGetB(borderColor) - 20, 0);
5042
                bcDark = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
5043
                paint.setStrokeWidth(1);
5044
                paint.setColor(bcDark);
5045
                // Lines on the left
5046
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5047
                {
5048
                    int yt = i;
5049
                    int yb = ht - i;
5050
                    canvas.drawLine(i, yt, i, yb, paint);
5051
                }
5052
                // Lines on the top
5053
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5054
                {
5055
                    int xl = i;
5056
                    int xr = wt - i;
5057
                    canvas.drawLine(xl, i, xr, i, paint);
5058
                }
5059
                // Lines on right side
5060
                paint.setColor(bcLight);
40 andreas 5061
 
81 andreas 5062
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5063
                {
5064
                    int yt = i;
5065
                    int yb = ht - i;
5066
                    canvas.drawLine(wt - i, yt, wt - i, yb, paint);
5067
                }
5068
                // Lines on bottom
5069
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5070
                {
5071
                    int xl = i;
5072
                    int xr = wt - i;
5073
                    canvas.drawLine(xl, ht - i, xr, ht - i, paint);
5074
                }
5075
            break;
40 andreas 5076
 
81 andreas 5077
            case 31:    // Bevel Raised _L
5078
            case 33:    // Bevel Raised _M
5079
            case 35:    // Bevel Raised _S
192 andreas 5080
                mBorderWidth = sysBorders[borderIndex].width;
81 andreas 5081
                borderColor = TColor::getSkiaColor(sr[instance].cb);
5082
                red = std::min((int)SkColorGetR(borderColor) + 10, 255);
5083
                green = std::min((int)SkColorGetG(borderColor) + 10, 255);
5084
                blue = std::min((int)SkColorGetB(borderColor) + 10, 255);
5085
                bcLight = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
5086
                red = std::max((int)SkColorGetR(borderColor) - 10, 0);
5087
                green = std::max((int)SkColorGetG(borderColor) - 10, 0);
5088
                blue = std::max((int)SkColorGetB(borderColor) - 10, 0);
5089
                bcDark = SkColorSetARGB(SkColorGetA(borderColor), red, green, blue);
5090
                paint.setStrokeWidth(1);
5091
                paint.setColor(bcLight);
5092
                // Lines on the left
5093
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5094
                {
5095
                    int yt = i;
5096
                    int yb = ht - i;
5097
                    canvas.drawLine(i, yt, i, yb, paint);
5098
                }
5099
                // Lines on the top
5100
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5101
                {
5102
                    int xl = i;
5103
                    int xr = wt - i;
5104
                    canvas.drawLine(xl, i, xr, i, paint);
5105
                }
5106
                // Lines on right side
5107
                paint.setColor(bcDark);
5108
 
5109
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5110
                {
5111
                    int yt = i;
5112
                    int yb = ht - i;
5113
                    canvas.drawLine(wt - i, yt, wt - i, yb, paint);
5114
                }
5115
                // Lines on bottom
5116
                for (int i = 0; i < sysBorders[borderIndex].width; i++)
5117
                {
5118
                    int xl = i;
5119
                    int xr = wt - borderIndex;
5120
                    canvas.drawLine(xl, ht - i, xr, ht - i, paint);
5121
                }
5122
            break;
6 andreas 5123
        }
4 andreas 5124
    }
5125
 
3 andreas 5126
    return true;
5127
}
5128
 
8 andreas 5129
int TButton::numberLines(const string& str)
5130
{
5131
    DECL_TRACER("TButton::numberLines(const string& str)");
5132
 
5133
    int lines = 1;
5134
 
96 andreas 5135
    if (str.empty())
8 andreas 5136
    {
96 andreas 5137
        MSG_DEBUG("Found an empty string.");
5138
        return lines;
5139
    }
5140
 
5141
    string::const_iterator iter;
5142
 
118 andreas 5143
    for (iter = str.begin(); iter != str.end(); ++iter)
96 andreas 5144
    {
5145
        if (*iter == '\n')
8 andreas 5146
            lines++;
5147
    }
5148
 
5149
    return lines;
5150
}
5151
 
5152
SkRect TButton::calcRect(int width, int height, int pen)
5153
{
5154
    DECL_TRACER("TButton::calcRect(int width, int height, int pen)");
5155
    SkRect rect;
5156
 
5157
    SkScalar left = (SkScalar)pen / 2.0;
5158
    SkScalar top = (SkScalar)pen / 2.0;
101 andreas 5159
    SkScalar w = (SkScalar)width - (SkScalar)pen;
5160
    SkScalar h = (SkScalar)height - (SkScalar)pen;
8 andreas 5161
    rect.setXYWH(left, top, w, h);
5162
    return rect;
5163
}
5164
 
15 andreas 5165
void TButton::runAnimation()
8 andreas 5166
{
15 andreas 5167
    DECL_TRACER("TButton::runAnimation()");
8 andreas 5168
 
43 andreas 5169
    if (mAniRunning)
5170
        return;
5171
 
15 andreas 5172
    mAniRunning = true;
5173
    int instance = 0;
5174
    int max = (int)sr.size();
38 andreas 5175
    ulong tm = nu * ru + nd * rd;
15 andreas 5176
 
93 andreas 5177
    while (mAniRunning && !mAniStop && !prg_stopped)
15 andreas 5178
    {
38 andreas 5179
        mActInstance = instance;
176 andreas 5180
        mChanged = true;
38 andreas 5181
 
101 andreas 5182
        if (visible && !drawButton(instance))
15 andreas 5183
            break;
5184
 
5185
        instance++;
5186
 
5187
        if (instance >= max)
5188
            instance = 0;
5189
 
5190
        std::this_thread::sleep_for(std::chrono::milliseconds(tm));
5191
    }
5192
 
5193
    mAniRunning = false;
5194
}
5195
 
38 andreas 5196
void TButton::runAnimationRange(int start, int end, ulong step)
5197
{
5198
    DECL_TRACER("TButton::runAnimationRange(int start, int end, ulong step)");
5199
 
93 andreas 5200
    if (mAniRunning)
5201
        return;
5202
 
38 andreas 5203
    mAniRunning = true;
5204
    int instance = start;
5205
    int max = std::min(end, (int)sr.size());
5206
    std::chrono::steady_clock::time_point startt = std::chrono::steady_clock::now();
5207
 
93 andreas 5208
    while (mAniRunning && !mAniStop && !prg_stopped)
38 andreas 5209
    {
5210
        mActInstance = instance;
177 andreas 5211
        mChanged = true;
101 andreas 5212
 
5213
        if (visible)
5214
            drawButton(instance);   // We ignore the state and try to draw the next instance
5215
 
38 andreas 5216
        instance++;
5217
 
5218
        if (instance >= max)
5219
            instance = start;
5220
 
5221
        std::this_thread::sleep_for(std::chrono::milliseconds(step));
5222
 
5223
        if (mAniRunTime > 0)
5224
        {
5225
            std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
5226
            std::chrono::nanoseconds difftime = current - startt;
5227
            ulong duration = std::chrono::duration_cast<std::chrono::milliseconds>(difftime).count();
5228
 
5229
            if (duration >= mAniRunTime)
5230
                break;
5231
        }
5232
    }
5233
 
5234
    mAniRunTime = 0;
5235
    mAniRunning = false;
5236
}
5237
 
15 andreas 5238
bool TButton::drawButtonMultistateAni()
5239
{
101 andreas 5240
    DECL_TRACER("TButton::drawButtonMultistateAni()");
15 andreas 5241
 
33 andreas 5242
    if (prg_stopped)
5243
        return true;
35 andreas 5244
 
100 andreas 5245
    if (!visible || hd)    // Do nothing if this button is invisible
5246
        return true;
5247
 
15 andreas 5248
    if (mAniRunning || mThrAni.joinable())
5249
    {
23 andreas 5250
        MSG_TRACE("Animation is already running!");
15 andreas 5251
        return true;
5252
    }
5253
 
5254
    try
5255
    {
93 andreas 5256
        mAniStop = false;
15 andreas 5257
        mThrAni = thread([=] { runAnimation(); });
5258
        mThrAni.detach();
5259
    }
5260
    catch (exception& e)
5261
    {
5262
        MSG_ERROR("Error starting the button animation thread: " << e.what());
5263
        return false;
5264
    }
5265
 
5266
    return true;
5267
}
5268
 
5269
bool TButton::drawButton(int instance, bool show)
5270
{
5271
    mutex_button.lock();
5272
    DECL_TRACER("TButton::drawButton(int instance, bool show)");
5273
 
33 andreas 5274
    if (prg_stopped)
34 andreas 5275
    {
5276
        mutex_button.unlock();
33 andreas 5277
        return false;
34 andreas 5278
    }
35 andreas 5279
 
8 andreas 5280
    if ((size_t)instance >= sr.size() || instance < 0)
5281
    {
5282
        MSG_ERROR("Instance " << instance << " is out of bounds!");
5283
        TError::setError();
15 andreas 5284
        mutex_button.unlock();
8 andreas 5285
        return false;
5286
    }
5287
 
26 andreas 5288
    if (!_displayButton && gPageManager)
5289
        _displayButton = gPageManager->getCallbackDB();
5290
 
100 andreas 5291
    if (!visible || hd || instance != mActInstance || !_displayButton)
14 andreas 5292
    {
15 andreas 5293
        bool db = (_displayButton != nullptr);
23 andreas 5294
        MSG_DEBUG("Button " << bi << ", \"" << na << "\" at instance " << instance << " is not to draw!");
150 andreas 5295
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Hidden: " << (hd ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
15 andreas 5296
        mutex_button.unlock();
14 andreas 5297
        return true;
5298
    }
5299
 
137 andreas 5300
    MSG_DEBUG("Drawing button " << bi << ", \"" << na << "\" at instance " << instance);
176 andreas 5301
 
5302
    if (!mChanged && !mLastImage.empty())
5303
    {
5304
        showLastButton();
5305
        mutex_button.unlock();
5306
        return true;
5307
    }
5308
 
8 andreas 5309
    ulong parent = mHandle & 0xffff0000;
5310
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
5311
 
5312
    if (TError::isError())
15 andreas 5313
    {
5314
        mutex_button.unlock();
8 andreas 5315
        return false;
15 andreas 5316
    }
8 andreas 5317
 
5318
    SkBitmap imgButton;
10 andreas 5319
    imgButton.allocN32Pixels(wt, ht);
97 andreas 5320
    bool dynState = false;
8 andreas 5321
 
5322
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
5323
    {
5324
        if (mDOrder[i] == ORD_ELEM_FILL)
5325
        {
5326
            if (!buttonFill(&imgButton, instance))
15 andreas 5327
            {
5328
                mutex_button.unlock();
8 andreas 5329
                return false;
15 andreas 5330
            }
8 andreas 5331
        }
5332
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
5333
        {
21 andreas 5334
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
15 andreas 5335
            {
97 andreas 5336
                MSG_DEBUG("Button reported an error. Abort drawing!");
15 andreas 5337
                mutex_button.unlock();
8 andreas 5338
                return false;
15 andreas 5339
            }
97 andreas 5340
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, show, &dynState))
21 andreas 5341
            {
97 andreas 5342
                MSG_DEBUG("Dynamic button reported an error. Abort drawing!");
21 andreas 5343
                mutex_button.unlock();
5344
                return false;
5345
            }
8 andreas 5346
        }
5347
        else if (mDOrder[i] == ORD_ELEM_ICON)
5348
        {
5349
            if (!buttonIcon(&imgButton, instance))
15 andreas 5350
            {
5351
                mutex_button.unlock();
8 andreas 5352
                return false;
15 andreas 5353
            }
8 andreas 5354
        }
5355
        else if (mDOrder[i] == ORD_ELEM_TEXT)
5356
        {
5357
            if (!buttonText(&imgButton, instance))
15 andreas 5358
            {
5359
                mutex_button.unlock();
8 andreas 5360
                return false;
15 andreas 5361
            }
8 andreas 5362
        }
5363
        else if (mDOrder[i] == ORD_ELEM_BORDER)
5364
        {
5365
            if (!buttonBorder(&imgButton, instance))
15 andreas 5366
            {
5367
                mutex_button.unlock();
8 andreas 5368
                return false;
15 andreas 5369
            }
8 andreas 5370
        }
5371
    }
5372
 
10 andreas 5373
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
5374
    {
5375
        SkBitmap ooButton;
5376
        int w = imgButton.width();
5377
        int h = imgButton.height();
5378
        ooButton.allocN32Pixels(w, h, true);
5379
        SkCanvas canvas(ooButton);
5380
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
5381
        SkRegion region;
5382
        region.setRect(irect);
5383
        SkScalar oo;
5384
 
5385
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
5386
        {
5387
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
5388
            MSG_DEBUG("Set global overal opacity to " << oo);
5389
        }
5390
        else if (sr[instance].oo >= 0)
5391
        {
5392
            oo = (SkScalar)sr[instance].oo;
5393
            MSG_DEBUG("Set overal opacity to " << oo);
5394
        }
5395
        else
5396
        {
5397
            oo = (SkScalar)mGlobalOO;
5398
            MSG_DEBUG("Set global overal opacity to " << oo);
5399
        }
5400
 
5401
        SkScalar alpha = 1.0 / 255.0 * oo;
5402
        MSG_DEBUG("Calculated alpha value: " << alpha);
5403
        SkPaint paint;
5404
        paint.setAlphaf(alpha);
5405
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
179 andreas 5406
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgButton);
5407
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
10 andreas 5408
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
5409
        imgButton = ooButton;
5410
    }
5411
 
15 andreas 5412
    mLastImage = imgButton;
177 andreas 5413
    mChanged = false;
8 andreas 5414
    size_t rowBytes = imgButton.info().minRowBytes();
5415
 
97 andreas 5416
    if (!prg_stopped && !dynState)
26 andreas 5417
    {
5418
        int rwidth = wt;
5419
        int rheight = ht;
5420
        int rleft = lt;
5421
        int rtop = tp;
43 andreas 5422
#ifdef _SCALE_SKIA_
26 andreas 5423
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5424
        {
5425
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5426
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5427
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5428
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
5429
 
5430
            SkPaint paint;
5431
            paint.setBlendMode(SkBlendMode::kSrc);
5432
            paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 5433
            // Calculate new dimension
26 andreas 5434
            SkImageInfo info = imgButton.info();
5435
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
5436
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
28 andreas 5437
            // Create a canvas and draw new image
5438
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
5439
            imgButton.allocN32Pixels(width, height);
5440
            imgButton.eraseColor(SK_ColorTRANSPARENT);
26 andreas 5441
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5442
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
179 andreas 5443
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
28 andreas 5444
            rowBytes = imgButton.info().minRowBytes();
5445
            mLastImage = imgButton;
26 andreas 5446
        }
43 andreas 5447
#endif
31 andreas 5448
        if (show)
176 andreas 5449
        {
31 andreas 5450
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
176 andreas 5451
        }
26 andreas 5452
    }
5453
 
15 andreas 5454
    mutex_button.unlock();
8 andreas 5455
    return true;
5456
}
5457
 
50 andreas 5458
bool TButton::drawTextArea(int instance)
5459
{
192 andreas 5460
    DECL_TRACER("TButton::drawTextArea(int instance)");
50 andreas 5461
 
5462
    if (prg_stopped)
5463
        return false;
5464
 
100 andreas 5465
    if (!visible || hd)
5466
        return true;
5467
 
50 andreas 5468
    if ((size_t)instance >= sr.size() || instance < 0)
5469
    {
5470
        MSG_ERROR("Instance " << instance << " is out of bounds!");
5471
        TError::setError();
5472
        return false;
5473
    }
5474
 
176 andreas 5475
    if (!mChanged)
5476
    {
5477
        showLastButton();
5478
        return true;
5479
    }
5480
 
50 andreas 5481
    getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
5482
 
5483
    if (TError::isError())
5484
        return false;
5485
 
5486
    SkBitmap imgButton;
5487
    imgButton.allocN32Pixels(wt, ht);
5488
 
5489
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
5490
    {
5491
        if (mDOrder[i] == ORD_ELEM_FILL)
5492
        {
5493
            if (!buttonFill(&imgButton, instance))
5494
                return false;
5495
        }
5496
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
5497
        {
5498
            if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
5499
                return false;
97 andreas 5500
            else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, false))
50 andreas 5501
                return false;
5502
        }
5503
        else if (mDOrder[i] == ORD_ELEM_ICON)
5504
        {
5505
            if (!buttonIcon(&imgButton, instance))
5506
                return false;
5507
        }
5508
        else if (mDOrder[i] == ORD_ELEM_BORDER)
5509
        {
5510
            if (!buttonBorder(&imgButton, instance))
5511
                return false;
5512
        }
5513
    }
5514
 
5515
    if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
5516
    {
5517
        SkBitmap ooButton;
5518
        int w = imgButton.width();
5519
        int h = imgButton.height();
5520
        ooButton.allocN32Pixels(w, h, true);
5521
        SkCanvas canvas(ooButton);
5522
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
5523
        SkRegion region;
5524
        region.setRect(irect);
5525
        SkScalar oo;
5526
 
5527
        if (mGlobalOO >= 0 && sr[instance].oo >= 0)
5528
        {
5529
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
5530
            MSG_DEBUG("Set global overal opacity to " << oo);
5531
        }
5532
        else if (sr[instance].oo >= 0)
5533
        {
5534
            oo = (SkScalar)sr[instance].oo;
5535
            MSG_DEBUG("Set overal opacity to " << oo);
5536
        }
5537
        else
5538
        {
5539
            oo = (SkScalar)mGlobalOO;
5540
            MSG_DEBUG("Set global overal opacity to " << oo);
5541
        }
5542
 
5543
        SkScalar alpha = 1.0 / 255.0 * oo;
5544
        MSG_DEBUG("Calculated alpha value: " << alpha);
5545
        SkPaint paint;
5546
        paint.setAlphaf(alpha);
5547
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
179 andreas 5548
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgButton);
5549
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
50 andreas 5550
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
5551
        imgButton = ooButton;
5552
    }
5553
 
5554
    mLastImage = imgButton;
177 andreas 5555
    mChanged = false;
50 andreas 5556
 
52 andreas 5557
    if (!prg_stopped)
50 andreas 5558
    {
5559
        int rwidth = wt;
5560
        int rheight = ht;
5561
        int rleft = lt;
5562
        int rtop = tp;
5563
        size_t rowBytes = imgButton.info().minRowBytes();
52 andreas 5564
#ifdef _SCALE_SKIA_
5565
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5566
        {
5567
            size_t rowBytes = imgButton.info().minRowBytes();
5568
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5569
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5570
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5571
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
50 andreas 5572
 
52 andreas 5573
            SkPaint paint;
5574
            paint.setBlendMode(SkBlendMode::kSrc);
5575
            paint.setFilterQuality(kHigh_SkFilterQuality);
5576
            // Calculate new dimension
5577
            SkImageInfo info = imgButton.info();
5578
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
5579
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
5580
            // Create a canvas and draw new image
5581
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
5582
            imgButton.allocN32Pixels(width, height);
5583
            imgButton.eraseColor(SK_ColorTRANSPARENT);
5584
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5585
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
179 andreas 5586
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
52 andreas 5587
            rowBytes = imgButton.info().minRowBytes();
5588
            mLastImage = imgButton;
5589
        }
5590
#endif
5591
        if (gPageManager && gPageManager->getCallbackInputText())
5592
        {
5593
            BITMAP_t bm;
5594
            bm.buffer = (unsigned char *)imgButton.getPixels();
5595
            bm.rowBytes = rowBytes;
5596
            bm.left = rleft;
5597
            bm.top = rtop;
5598
            bm.width = rwidth;
5599
            bm.height = rheight;
192 andreas 5600
            gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
52 andreas 5601
        }
50 andreas 5602
    }
5603
 
5604
    return true;
5605
}
5606
 
38 andreas 5607
bool TButton::drawMultistateBargraph(int level, bool show)
5608
{
5609
    mutex_button.lock();
5610
    DECL_TRACER("TButton::drawMultistateBargraph(int level, bool show)");
5611
 
5612
    if (prg_stopped)
5613
    {
5614
        mutex_button.unlock();
5615
        return false;
5616
    }
5617
 
5618
    if (!_displayButton && gPageManager)
5619
        _displayButton = gPageManager->getCallbackDB();
5620
 
100 andreas 5621
    if (!visible || hd || !_displayButton)
38 andreas 5622
    {
5623
        bool db = (_displayButton != nullptr);
5624
        MSG_DEBUG("Multistate bargraph " << bi << ", \"" << na << " is not to draw!");
5625
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
5626
        mutex_button.unlock();
5627
        return true;
5628
    }
5629
 
5630
    int maxLevel = level;
5631
 
5632
    if (maxLevel >= rh)
5633
        maxLevel = rh - 1 ;
45 andreas 5634
    else if (maxLevel <= rl && rl > 0)
38 andreas 5635
        maxLevel = rl - 1;
45 andreas 5636
    else if (maxLevel < 0)
5637
        maxLevel = 0;
38 andreas 5638
 
46 andreas 5639
    MSG_DEBUG("Display instance " << maxLevel);
38 andreas 5640
    ulong parent = mHandle & 0xffff0000;
5641
    getDrawOrder(sr[maxLevel]._do, (DRAW_ORDER *)&mDOrder);
5642
 
5643
    if (TError::isError())
5644
    {
5645
        mutex_button.unlock();
5646
        return false;
5647
    }
5648
 
5649
    SkBitmap imgButton;
5650
    imgButton.allocN32Pixels(wt, ht);
5651
 
5652
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
5653
    {
5654
        if (mDOrder[i] == ORD_ELEM_FILL)
5655
        {
5656
            if (!buttonFill(&imgButton, maxLevel))
5657
            {
5658
                mutex_button.unlock();
5659
                return false;
5660
            }
5661
        }
5662
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
5663
        {
5664
            if (!buttonBitmap(&imgButton, maxLevel))
5665
            {
5666
                mutex_button.unlock();
5667
                return false;
5668
            }
5669
        }
5670
        else if (mDOrder[i] == ORD_ELEM_ICON)
5671
        {
5672
            if (!buttonIcon(&imgButton, maxLevel))
5673
            {
5674
                mutex_button.unlock();
5675
                return false;
5676
            }
5677
        }
5678
        else if (mDOrder[i] == ORD_ELEM_TEXT)
5679
        {
5680
            if (!buttonText(&imgButton, maxLevel))
5681
            {
5682
                mutex_button.unlock();
5683
                return false;
5684
            }
5685
        }
5686
        else if (mDOrder[i] == ORD_ELEM_BORDER)
5687
        {
5688
            if (!buttonBorder(&imgButton, maxLevel))
5689
            {
5690
                mutex_button.unlock();
5691
                return false;
5692
            }
5693
        }
5694
    }
5695
 
5696
    if (mGlobalOO >= 0 || sr[maxLevel].oo >= 0) // Take overall opacity into consideration
5697
    {
5698
        SkBitmap ooButton;
5699
        int w = imgButton.width();
5700
        int h = imgButton.height();
5701
        ooButton.allocN32Pixels(w, h, true);
5702
        SkCanvas canvas(ooButton);
5703
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
5704
        SkRegion region;
5705
        region.setRect(irect);
5706
        SkScalar oo;
5707
 
5708
        if (mGlobalOO >= 0 && sr[maxLevel].oo >= 0)
5709
        {
5710
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[maxLevel].oo);
5711
            MSG_DEBUG("Set global overal opacity to " << oo);
5712
        }
5713
        else if (sr[maxLevel].oo >= 0)
5714
        {
5715
            oo = (SkScalar)sr[maxLevel].oo;
5716
            MSG_DEBUG("Set overal opacity to " << oo);
5717
        }
5718
        else
5719
        {
5720
            oo = (SkScalar)mGlobalOO;
5721
            MSG_DEBUG("Set global overal opacity to " << oo);
5722
        }
5723
 
5724
        SkScalar alpha = 1.0 / 255.0 * oo;
5725
        MSG_DEBUG("Calculated alpha value: " << alpha);
5726
        SkPaint paint;
5727
        paint.setAlphaf(alpha);
5728
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
179 andreas 5729
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgButton);
5730
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
38 andreas 5731
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
5732
        imgButton = ooButton;
5733
    }
5734
 
5735
    mLastImage = imgButton;
177 andreas 5736
    mChanged = false;
38 andreas 5737
    size_t rowBytes = imgButton.info().minRowBytes();
5738
 
5739
    if (!prg_stopped)
5740
    {
5741
        int rwidth = wt;
5742
        int rheight = ht;
5743
        int rleft = lt;
5744
        int rtop = tp;
43 andreas 5745
#ifdef _SCALE_SKIA_
38 andreas 5746
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5747
        {
5748
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5749
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5750
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5751
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
5752
 
5753
            SkPaint paint;
5754
            paint.setBlendMode(SkBlendMode::kSrc);
5755
            paint.setFilterQuality(kHigh_SkFilterQuality);
5756
            // Calculate new dimension
5757
            SkImageInfo info = imgButton.info();
5758
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
5759
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
5760
            MSG_DEBUG("Button dimension: " << width << " x " << height);
5761
            // Create a canvas and draw new image
5762
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
5763
            imgButton.allocN32Pixels(width, height);
5764
            imgButton.eraseColor(SK_ColorTRANSPARENT);
5765
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5766
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
179 andreas 5767
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
38 andreas 5768
            MSG_DEBUG("Old rowBytes: " << rowBytes);
5769
            rowBytes = imgButton.info().minRowBytes();
5770
            MSG_DEBUG("New rowBytes: " << rowBytes);
5771
            mLastImage = imgButton;
5772
        }
43 andreas 5773
#endif
38 andreas 5774
        if (show)
5775
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
5776
    }
5777
 
5778
    mutex_button.unlock();
5779
    return true;
5780
}
5781
 
200 andreas 5782
bool TButton::drawList(bool show)
5783
{
5784
    DECL_TRACER("TButton::drawList(bool show)");
5785
 
5786
    if (!mChanged)
5787
    {
5788
        showLastButton();
5789
        return true;
5790
    }
5791
 
5792
    getDrawOrder(sr[0]._do, (DRAW_ORDER *)&mDOrder);
5793
 
5794
    if (TError::isError())
5795
        return false;
5796
 
5797
    SkBitmap imgButton;
5798
    imgButton.allocN32Pixels(wt, ht);
5799
 
5800
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
5801
    {
5802
        if (mDOrder[i] == ORD_ELEM_FILL)
5803
        {
5804
            if (!buttonFill(&imgButton, 0))
5805
                return false;
5806
        }
5807
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
5808
        {
5809
            if (!sr[0].dynamic && !buttonBitmap(&imgButton, 0))
5810
                return false;
5811
            else if (sr[0].dynamic && !buttonDynamic(&imgButton, 0, false))
5812
                return false;
5813
        }
5814
        else if (mDOrder[i] == ORD_ELEM_ICON)
5815
        {
5816
            if (!buttonIcon(&imgButton, 0))
5817
                return false;
5818
        }
5819
        else if (mDOrder[i] == ORD_ELEM_BORDER)
5820
        {
5821
            if (!buttonBorder(&imgButton, 0))
5822
                return false;
5823
        }
5824
    }
5825
 
5826
    if (mGlobalOO >= 0 || sr[0].oo >= 0) // Take overall opacity into consideration
5827
    {
5828
        SkBitmap ooButton;
5829
        int w = imgButton.width();
5830
        int h = imgButton.height();
5831
        ooButton.allocN32Pixels(w, h, true);
5832
        SkCanvas canvas(ooButton);
5833
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
5834
        SkRegion region;
5835
        region.setRect(irect);
5836
        SkScalar oo;
5837
 
5838
        if (mGlobalOO >= 0 && sr[0].oo >= 0)
5839
        {
5840
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[0].oo);
5841
            MSG_DEBUG("Set global overal opacity to " << oo);
5842
        }
5843
        else if (sr[0].oo >= 0)
5844
        {
5845
            oo = (SkScalar)sr[0].oo;
5846
            MSG_DEBUG("Set overal opacity to " << oo);
5847
        }
5848
        else
5849
        {
5850
            oo = (SkScalar)mGlobalOO;
5851
            MSG_DEBUG("Set global overal opacity to " << oo);
5852
        }
5853
 
5854
        SkScalar alpha = 1.0 / 255.0 * oo;
5855
        MSG_DEBUG("Calculated alpha value: " << alpha);
5856
        SkPaint paint;
5857
        paint.setAlphaf(alpha);
5858
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
5859
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgButton);
5860
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
5861
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
5862
        imgButton = ooButton;
5863
    }
5864
 
5865
    mLastImage = imgButton;
5866
    mChanged = false;
5867
 
5868
    if (!prg_stopped)
5869
    {
5870
        int rwidth = wt;
5871
        int rheight = ht;
5872
        int rleft = lt;
5873
        int rtop = tp;
5874
        size_t rowBytes = imgButton.info().minRowBytes();
5875
#ifdef _SCALE_SKIA_
5876
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
5877
        {
5878
            size_t rowBytes = imgButton.info().minRowBytes();
5879
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
5880
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
5881
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
5882
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
5883
 
5884
            SkPaint paint;
5885
            paint.setBlendMode(SkBlendMode::kSrc);
5886
            paint.setFilterQuality(kHigh_SkFilterQuality);
5887
            // Calculate new dimension
5888
            SkImageInfo info = imgButton.info();
5889
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
5890
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
5891
            // Create a canvas and draw new image
5892
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
5893
            imgButton.allocN32Pixels(width, height);
5894
            imgButton.eraseColor(SK_ColorTRANSPARENT);
5895
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
5896
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
5897
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
5898
            rowBytes = imgButton.info().minRowBytes();
5899
            mLastImage = imgButton;
5900
        }
5901
#endif
5902
        if (show && gPageManager && gPageManager->getCallbackListBox())
5903
        {
5904
            BITMAP_t bm;
5905
            bm.buffer = (unsigned char *)imgButton.getPixels();
5906
            bm.rowBytes = rowBytes;
5907
            bm.left = rleft;
5908
            bm.top = rtop;
5909
            bm.width = rwidth;
5910
            bm.height = rheight;
5911
            gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
5912
        }
5913
    }
5914
 
5915
    return true;
5916
}
5917
 
15 andreas 5918
bool TButton::drawBargraph(int instance, int level, bool show)
5919
{
5920
    mutex_bargraph.lock();
177 andreas 5921
    DECL_TRACER("TButton::drawBargraph(int instance, int level, bool show)");
15 andreas 5922
 
5923
    if ((size_t)instance >= sr.size() || instance < 0)
5924
    {
5925
        MSG_ERROR("Instance " << instance << " is out of bounds!");
5926
        TError::setError();
5927
        mutex_bargraph.unlock();
5928
        return false;
5929
    }
5930
 
38 andreas 5931
    if (!_displayButton && gPageManager)
5932
        _displayButton = gPageManager->getCallbackDB();
5933
 
176 andreas 5934
    if (!mChanged && mLastLevel == level)
5935
    {
5936
        showLastButton();
5937
        mutex_bargraph.unlock();
5938
        return true;
5939
    }
5940
 
15 andreas 5941
    if (level < rl)
5942
        mLastLevel = rl;
5943
    else if (level > rh)
5944
        mLastLevel = rh;
5945
    else
5946
        mLastLevel = level;
5947
 
38 andreas 5948
    int inst = instance;
5949
 
100 andreas 5950
    if (!visible || hd || instance != mActInstance || !_displayButton)
15 andreas 5951
    {
5952
        bool db = (_displayButton != nullptr);
23 andreas 5953
        MSG_DEBUG("Bargraph " << bi << ", \"" << na << "\" at instance " << instance << " with level " << mLastLevel << " is not to draw!");
15 andreas 5954
        MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
5955
        mutex_bargraph.unlock();
5956
        return true;
5957
    }
5958
 
5959
    ulong parent = mHandle & 0xffff0000;
35 andreas 5960
 
20 andreas 5961
    if (type == BARGRAPH)
38 andreas 5962
    {
20 andreas 5963
        getDrawOrder(sr[1]._do, (DRAW_ORDER *)&mDOrder);
38 andreas 5964
        inst = 1;
5965
    }
20 andreas 5966
    else
5967
        getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
35 andreas 5968
 
15 andreas 5969
    if (TError::isError())
5970
    {
5971
        mutex_bargraph.unlock();
5972
        return false;
5973
    }
5974
 
5975
    SkBitmap imgButton;
5976
    imgButton.allocN32Pixels(wt, ht);
38 andreas 5977
    imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
5978
    bool haveFrame = false;
15 andreas 5979
 
5980
    for (int i = 0; i < ORD_ELEM_COUNT; i++)
5981
    {
38 andreas 5982
        if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
15 andreas 5983
        {
38 andreas 5984
            if (!buttonFill(&imgButton, (type == BARGRAPH ? 0 : inst)))
15 andreas 5985
            {
5986
                mutex_bargraph.unlock();
5987
                return false;
5988
            }
5989
        }
5990
        else if (mDOrder[i] == ORD_ELEM_BITMAP)
5991
        {
38 andreas 5992
            if (!barLevel(&imgButton, inst, mLastLevel))
15 andreas 5993
            {
5994
                mutex_bargraph.unlock();
5995
                return false;
5996
            }
5997
        }
5998
        else if (mDOrder[i] == ORD_ELEM_ICON)
5999
        {
38 andreas 6000
            if (!buttonIcon(&imgButton, inst))
15 andreas 6001
            {
6002
                mutex_bargraph.unlock();
6003
                return false;
6004
            }
6005
        }
6006
        else if (mDOrder[i] == ORD_ELEM_TEXT)
6007
        {
38 andreas 6008
            if (!buttonText(&imgButton, inst))
15 andreas 6009
            {
6010
                mutex_bargraph.unlock();
6011
                return false;
6012
            }
6013
        }
6014
        else if (mDOrder[i] == ORD_ELEM_BORDER)
6015
        {
38 andreas 6016
            if (!buttonBorder(&imgButton, (type == BARGRAPH ? 0 : inst)))
15 andreas 6017
            {
6018
                mutex_bargraph.unlock();
6019
                return false;
6020
            }
38 andreas 6021
 
6022
            haveFrame = true;
15 andreas 6023
        }
6024
    }
6025
 
38 andreas 6026
    if (mGlobalOO >= 0 || sr[inst].oo >= 0) // Take overall opacity into consideration
15 andreas 6027
    {
6028
        SkBitmap ooButton;
6029
        int w = imgButton.width();
6030
        int h = imgButton.height();
6031
        ooButton.allocN32Pixels(w, h, true);
6032
        SkCanvas canvas(ooButton);
6033
        SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
6034
        SkRegion region;
6035
        region.setRect(irect);
6036
        SkScalar oo;
6037
 
38 andreas 6038
        if (mGlobalOO >= 0 && sr[inst].oo >= 0)
15 andreas 6039
        {
38 andreas 6040
            oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[inst].oo);
15 andreas 6041
            MSG_DEBUG("Set global overal opacity to " << oo);
6042
        }
38 andreas 6043
        else if (sr[inst].oo >= 0)
15 andreas 6044
        {
38 andreas 6045
            oo = (SkScalar)sr[inst].oo;
15 andreas 6046
            MSG_DEBUG("Set overal opacity to " << oo);
6047
        }
6048
        else
6049
        {
6050
            oo = (SkScalar)mGlobalOO;
6051
            MSG_DEBUG("Set global overal opacity to " << oo);
6052
        }
6053
 
6054
        SkScalar alpha = 1.0 / 255.0 * oo;
6055
        MSG_DEBUG("Calculated alpha value: " << alpha);
6056
        SkPaint paint;
6057
        paint.setAlphaf(alpha);
6058
        paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
179 andreas 6059
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(imgButton);
6060
        canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
15 andreas 6061
        imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
6062
        imgButton = ooButton;
6063
    }
6064
 
6065
    mLastImage = imgButton;
177 andreas 6066
    mChanged = false;
15 andreas 6067
    size_t rowBytes = imgButton.info().minRowBytes();
6068
 
21 andreas 6069
    if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
26 andreas 6070
    {
6071
        int rwidth = wt;
6072
        int rheight = ht;
6073
        int rleft = lt;
6074
        int rtop = tp;
43 andreas 6075
#ifdef _SCALE_SKIA_
26 andreas 6076
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6077
        {
6078
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6079
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6080
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
6081
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
6082
 
6083
            SkPaint paint;
6084
            paint.setBlendMode(SkBlendMode::kSrc);
6085
            paint.setFilterQuality(kHigh_SkFilterQuality);
28 andreas 6086
            // Calculate new dimension
26 andreas 6087
            SkImageInfo info = imgButton.info();
6088
            int width = (int)((double)info.width() * gPageManager->getScaleFactor());
6089
            int height = (int)((double)info.height() * gPageManager->getScaleFactor());
28 andreas 6090
            // Create a canvas and draw new image
6091
            sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
6092
            imgButton.allocN32Pixels(width, height);
6093
            imgButton.eraseColor(SK_ColorTRANSPARENT);
26 andreas 6094
            SkCanvas can(imgButton, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
6095
            SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
179 andreas 6096
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
28 andreas 6097
            rowBytes = imgButton.info().minRowBytes();
6098
            mLastImage = imgButton;
26 andreas 6099
        }
43 andreas 6100
#endif
26 andreas 6101
        _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
6102
    }
6103
 
15 andreas 6104
    mutex_bargraph.unlock();
6105
    return true;
6106
}
6107
 
40 andreas 6108
POSITION_t TButton::calcImagePosition(int width, int height, CENTER_CODE cc, int number, int line)
4 andreas 6109
{
6110
    DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
6111
 
6112
    SR_T act_sr;
6113
    POSITION_t position;
69 andreas 6114
    int ix, iy, ln;
4 andreas 6115
 
6116
    if (sr.size() == 0)
6117
        return position;
6118
 
6119
    if (number <= 0)
6120
        act_sr = sr.at(0);
6121
    else if ((size_t)number < sr.size())
6122
        act_sr = sr.at(number);
46 andreas 6123
    else if ((size_t)number >= sr.size())
6124
        act_sr = sr.at(sr.size() - 1);
4 andreas 6125
    else
6126
        return position;
6127
 
69 andreas 6128
    if (line <= 0)
6129
        ln = 1;
6130
    else
6131
        ln = line;
6132
 
4 andreas 6133
    int border_size = getBorderSize(act_sr.bs);
8 andreas 6134
    int code, border = border_size;
10 andreas 6135
    string dbgCC;
17 andreas 6136
    int rwt = 0, rht = 0;
4 andreas 6137
 
6138
    switch (cc)
3 andreas 6139
    {
8 andreas 6140
        case SC_ICON:
6141
            code = act_sr.ji;
6142
            ix = act_sr.ix;
6143
            iy = act_sr.iy;
6144
            border = border_size = 0;
10 andreas 6145
            dbgCC = "ICON";
17 andreas 6146
            rwt = width;
6147
            rht = height;
8 andreas 6148
        break;
6149
 
6150
        case SC_BITMAP:
6151
            code = act_sr.jb;
10 andreas 6152
            ix = act_sr.bx;
6153
            iy = act_sr.by;
6154
            dbgCC = "BITMAP";
17 andreas 6155
            rwt = std::min(wt - border * 2, width);
6156
            rht = std::min(ht - border_size * 2, height);
8 andreas 6157
        break;
6158
 
6159
        case SC_TEXT:
6160
            code = act_sr.jt;
6161
            ix = act_sr.tx;
6162
            iy = act_sr.ty;
10 andreas 6163
            dbgCC = "TEXT";
164 andreas 6164
 
6165
            if (border < 4)
6166
                border = 4;
6167
//            border += 4;
69 andreas 6168
            rwt = std::min(wt - border * 2, width);         // We've always a minimum (invisible) border of 4 pixels.
6169
            rht = std::min(ht - border_size * 2, height);   // The height is calculated from a defined border, if any.
8 andreas 6170
        break;
3 andreas 6171
    }
4 andreas 6172
 
17 andreas 6173
    if (width > rwt || height > rht)
6174
        position.overflow = true;
46 andreas 6175
 
4 andreas 6176
    switch (code)
6177
    {
6178
        case 0: // absolute position
8 andreas 6179
            position.left = ix;
69 andreas 6180
            position.top = iy;
40 andreas 6181
 
46 andreas 6182
            if (cc == SC_BITMAP && ix < 0 && rwt < width)
6183
                position.left *= -1;
6184
 
6185
            if (cc == SC_BITMAP && iy < 0 && rht < height)
6186
                position.top += -1;
6187
 
10 andreas 6188
            position.width = rwt;
6189
            position.height = rht;
4 andreas 6190
        break;
6191
 
6192
        case 1: // top, left
10 andreas 6193
            if (cc == SC_TEXT)
40 andreas 6194
            {
10 andreas 6195
                position.left = border;
96 andreas 6196
                position.top = ht - ((ht - rht) / 2) - height * ln;
40 andreas 6197
            }
10 andreas 6198
 
6199
            position.width = rwt;
6200
            position.height = rht;
4 andreas 6201
        break;
6202
 
6203
        case 2: // center, top
40 andreas 6204
            if (cc == SC_TEXT)
96 andreas 6205
                position.top = ht - ((ht - rht) / 2) - height * ln;
40 andreas 6206
 
10 andreas 6207
            position.left = (wt - rwt) / 2;
6208
            position.height = rht;
6209
            position.width = rwt;
4 andreas 6210
        break;
6211
 
6212
        case 3: // right, top
10 andreas 6213
            position.left = wt - rwt;
19 andreas 6214
 
6215
            if (cc == SC_TEXT)
40 andreas 6216
            {
19 andreas 6217
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
96 andreas 6218
                position.top = ht - (ht - rht) - height * ln;
40 andreas 6219
            }
19 andreas 6220
 
10 andreas 6221
            position.width = rwt;
6222
            position.height = rht;
4 andreas 6223
        break;
6224
 
6225
        case 4: // left, middle
10 andreas 6226
            if (cc == SC_TEXT)
40 andreas 6227
            {
10 andreas 6228
                position.left = border;
69 andreas 6229
                position.top = (ht - height) / 2;
40 andreas 6230
            }
6231
            else
6232
                position.top = (ht - rht) / 2;
10 andreas 6233
 
6234
            position.width = rwt;
6235
            position.height = rht;
4 andreas 6236
        break;
6237
 
6238
        case 6: // right, middle
10 andreas 6239
            position.left = wt - rwt;
19 andreas 6240
 
6241
            if (cc == SC_TEXT)
40 andreas 6242
            {
19 andreas 6243
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
69 andreas 6244
                position.top = (ht - height) / 2;
40 andreas 6245
            }
6246
            else
6247
                position.top = (ht - rht) / 2;
19 andreas 6248
 
10 andreas 6249
            position.width = rwt;
6250
            position.height = rht;
4 andreas 6251
        break;
6252
 
6253
        case 7: // left, bottom
10 andreas 6254
            if (cc == SC_TEXT)
40 andreas 6255
            {
10 andreas 6256
                position.left = border_size;
69 andreas 6257
                position.top = (ht - rht) - height * ln;
40 andreas 6258
            }
6259
            else
6260
                position.top = ht - rht;
10 andreas 6261
 
6262
            position.width = rwt;
6263
            position.height = rht;
4 andreas 6264
        break;
6265
 
6266
        case 8: // center, bottom
10 andreas 6267
            position.left = (wt - rwt) / 2;
40 andreas 6268
 
6269
            if (cc == SC_TEXT)
69 andreas 6270
                position.top = (ht - rht) - height * ln;
40 andreas 6271
            else
6272
                position.top = ht - rht;
6273
 
10 andreas 6274
            position.width = rwt;
6275
            position.height = rht;
4 andreas 6276
        break;
6277
 
6278
        case 9: // right, bottom
10 andreas 6279
            position.left = wt - rwt;
19 andreas 6280
 
6281
            if (cc == SC_TEXT)
40 andreas 6282
            {
19 andreas 6283
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
69 andreas 6284
                position.top = (ht - rht) - height * ln;
40 andreas 6285
            }
6286
            else
6287
                position.top = ht - rht;
4 andreas 6288
        break;
6289
 
6290
        default: // center, middle
10 andreas 6291
            position.left = (wt - rwt) / 2;
40 andreas 6292
 
6293
            if (cc == SC_TEXT)
69 andreas 6294
                position.top = (ht - height) / 2;
40 andreas 6295
            else
6296
                position.top = (ht - rht) / 2;
6297
 
10 andreas 6298
            position.width = rwt;
6299
            position.height = rht;
4 andreas 6300
    }
6301
 
69 andreas 6302
    if (TStreamError::checkFilter(HLOG_DEBUG))
6303
    {
70 andreas 6304
        string format = getFormatString((TEXT_ORIENTATION)code);
6305
        MSG_DEBUG("Type: " << dbgCC << ", format: " << format <<
69 andreas 6306
            ", PosType=" << code << ", total height=" << ht << ", height object=" << height <<
6307
            ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width <<
6308
            ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
6309
    }
6310
 
4 andreas 6311
    position.valid = true;
6312
    return position;
6313
}
6314
 
99 andreas 6315
IMAGE_SIZE_t TButton::calcImageSize(int imWidth, int imHeight, int instance, bool aspect)
6316
{
6317
    DECL_TRACER("TButton::calcImageSize(int imWidth, int imHeight, bool aspect)");
6318
 
6319
    int border = getBorderSize(sr[instance].bs);
6320
    IMAGE_SIZE_t isize;
6321
 
6322
    if (!aspect)
6323
    {
6324
        isize.width = wt - border * 2;
6325
        isize.height = ht - border * 2;
6326
    }
6327
    else
6328
    {
6329
        int w = wt - border * 2;
6330
        int h = ht - border * 2;
6331
        double scale;
6332
 
100 andreas 6333
        if (w < h || imWidth > imHeight)
99 andreas 6334
            scale = (double)w / (double)imWidth;
6335
        else
6336
            scale = (double)h / (double)imHeight;
6337
 
6338
        isize.width = (int)((double)imWidth * scale);
6339
        isize.height = (int)((double)imHeight * scale);
6340
    }
6341
 
6342
    MSG_DEBUG("Sizing image: Original: " << imWidth << " x " << imHeight << " to " << isize.width << " x " << isize.height);
6343
    return isize;
6344
}
6345
 
69 andreas 6346
string TButton::getFormatString(TEXT_ORIENTATION to)
6347
{
6348
    DECL_TRACER("TButton::getFormatString(CENTER_CODE cc)");
6349
 
6350
    switch(to)
6351
    {
6352
        case ORI_ABSOLUT:       return "ABSOLUT";
6353
        case ORI_BOTTOM_LEFT:   return "BOTTOM/LEFT";
6354
        case ORI_BOTTOM_MIDDLE: return "BOTTOM/MIDDLE";
6355
        case ORI_BOTTOM_RIGHT:  return "BOTTOM/RIGHT";
6356
        case ORI_CENTER_LEFT:   return "CENTER/LEFT";
6357
        case ORI_CENTER_MIDDLE: return "CENTER/MIDDLE";
6358
        case ORI_CENTER_RIGHT:  return "CENTER/RIGHT";
6359
        case ORI_TOP_LEFT:      return "TOP/LEFT";
6360
        case ORI_TOP_MIDDLE:    return "TOP/MIDDLE";
6361
        case ORI_TOP_RIGHT:     return "TOP/RIGHT";
6362
    }
6363
 
6364
    return "UNKNOWN";   // Should not happen!
6365
}
6366
 
4 andreas 6367
int TButton::getBorderSize(const std::string& name)
6368
{
6369
    DECL_TRACER("TButton::getBorderSize(const std::string& name)");
6370
 
81 andreas 6371
    if (gPageManager && gPageManager->getSystemDraw())
6372
    {
6373
        if (gPageManager->getSystemDraw()->existBorder(name))
6374
            return gPageManager->getSystemDraw()->getBorderWidth(name);
6375
    }
6376
 
4 andreas 6377
    for (int i = 0; sysBorders[i].name != nullptr; i++)
6378
    {
6379
        if (name.compare(sysBorders[i].name) == 0)
8 andreas 6380
        {
6381
            MSG_DEBUG("Border size: " << sysBorders[i].width);
4 andreas 6382
            return sysBorders[i].width;
8 andreas 6383
        }
4 andreas 6384
    }
6385
 
8 andreas 6386
    MSG_DEBUG("Border size: 0");
4 andreas 6387
    return 0;
6388
}
6389
 
6390
void TButton::calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)
6391
{
6392
    DECL_TRACER("TButton::clacImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)");
6393
 
6394
    int spX = btWidth - (btFrame * 2);
6395
    int spY = btHeight - (btFrame * 2);
6396
 
6397
    if (imWidth <= spX && imHeight <= spY)
6398
    {
6399
        *realX = imWidth;
6400
        *realY = imHeight;
6401
        return;
6402
    }
6403
 
6404
    int oversizeX = 0, oversizeY = 0;
6405
 
6406
    if (imWidth > spX)
6407
        oversizeX = imWidth - spX;
6408
 
6409
    if (imHeight > spY)
6410
        oversizeY = imHeight - spY;
6411
 
6412
    double percent = 0.0;
6413
 
6414
    if (oversizeX > oversizeY)
6415
        percent = 100.0 / (double)imWidth * (double)spX;
3 andreas 6416
    else
4 andreas 6417
        percent = 100.0 / (double)imHeight * (double)spY;
6418
 
6419
    *realX = (int)(percent / 100.0 * (double)imWidth);
6420
    *realY = (int)(percent / 100.0 * (double)imHeight);
6421
}
6422
 
10 andreas 6423
SkBitmap TButton::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
4 andreas 6424
{
6 andreas 6425
    DECL_TRACER("TButton::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
4 andreas 6426
 
35 andreas 6427
    if (width <= 0 || height <= 0)
6428
    {
165 andreas 6429
        MSG_WARNING("Got invalid width of height! (width: " << width << ", height: " << height << ")");
35 andreas 6430
        return SkBitmap();
6431
    }
6432
 
162 andreas 6433
    if (imgRed.empty())
6434
    {
6435
        MSG_WARNING("Missing mask to draw image!");
6436
        return SkBitmap();
6437
    }
6438
 
6 andreas 6439
    SkPixmap pixmapRed = imgRed.pixmap();
7 andreas 6440
    SkPixmap pixmapMask;
163 andreas 6441
    bool haveBothImages = true;
4 andreas 6442
 
7 andreas 6443
    if (!imgMask.empty())
6444
        pixmapMask = imgMask.pixmap();
163 andreas 6445
    else
6446
        haveBothImages = false;
162 andreas 6447
 
7 andreas 6448
    SkBitmap maskBm;
6449
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
69 andreas 6450
    maskBm.eraseColor(SK_ColorTRANSPARENT);
3 andreas 6451
 
4 andreas 6452
    for (int ix = 0; ix < width; ix++)
6453
    {
6454
        for (int iy = 0; iy < height; iy++)
3 andreas 6455
        {
184 andreas 6456
            SkColor pixelRed;
7 andreas 6457
            SkColor pixelMask;
3 andreas 6458
 
184 andreas 6459
            if (ix < pixmapRed.info().width() && iy < pixmapRed.info().height())
240 andreas 6460
                pixelRed = pixmapRed.getColor(ix, iy);
184 andreas 6461
            else
6462
                pixelRed = 0;
6463
 
6464
            if (haveBothImages && !imgMask.empty() &&
6465
                    ix < pixmapMask.info().width() && iy < pixmapMask.info().height())
7 andreas 6466
                pixelMask = pixmapMask.getColor(ix, iy);
6467
            else
164 andreas 6468
                pixelMask = SkColorSetA(SK_ColorWHITE, 0);
3 andreas 6469
 
10 andreas 6470
            SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
20 andreas 6471
            uint32_t alpha = SkColorGetA(pixel);
184 andreas 6472
            uint32_t *wpix = nullptr;
35 andreas 6473
 
184 andreas 6474
            if (ix < maskBm.info().width() && iy < maskBm.info().height())
6475
                wpix = maskBm.getAddr32(ix, iy);
6476
 
35 andreas 6477
            if (!wpix)
184 andreas 6478
                continue;
35 andreas 6479
 
20 andreas 6480
            if (alpha == 0)
6481
                pixel = pixelMask;
184 andreas 6482
 
35 andreas 6483
            *wpix = pixel;
3 andreas 6484
        }
6485
    }
6486
 
7 andreas 6487
    return maskBm;
3 andreas 6488
}
6 andreas 6489
 
99 andreas 6490
/**
6491
 * @brief Takes 2 images and combines them to one.
6492
 *
6493
 * The 2 images are a solid base image defining the basic form and an identical
6494
 * image defining the alpha channel.
6495
 *
6496
 * @param base  The base image containing the form as black pixels.
6497
 * @param alpha The image containing just an alpha channel.
6498
 * @param col   The color which should be used instead of a black pixel.
6499
 *
6500
 * @return On success a valid bitmap is returned containing the form.
6501
 * On error an empty bitmap is returned.
6502
 */
6503
SkBitmap TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)
6504
{
6505
    DECL_TRACER("TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)");
6506
 
6507
    int width = base.info().width();
6508
    int height = base.info().height();
6509
    SkBitmap Bm;    // The new bitmap. It will be returned in the end.
6510
 
6511
    if (width != alpha.info().width() || height != alpha.info().height())
6512
    {
6513
        MSG_ERROR("Mask and alpha have different size! [ " << width << " x " << height << " to " << alpha.info().width() << " x " << alpha.info().height());
6514
        return Bm;
6515
    }
6516
 
6517
    Bm.allocN32Pixels(width, height);
6518
    Bm.eraseColor(SK_ColorTRANSPARENT);
6519
 
6520
    for (int ix = 0; ix < width; ix++)
6521
    {
6522
        for (int iy = 0; iy < height; iy++)
6523
        {
6524
            SkColor pixelAlpha = alpha.getColor(ix, iy);
100 andreas 6525
            uint32_t *bpix = Bm.getAddr32(ix, iy);
99 andreas 6526
 
100 andreas 6527
            uchar al    = SkColorGetA(pixelAlpha);
99 andreas 6528
            uchar red   = SkColorGetR(col);
6529
            uchar green = SkColorGetG(col);
6530
            uchar blue  = SkColorGetB(col);
6531
 
100 andreas 6532
            if (pixelAlpha == 0)
6533
                red = green = blue = 0;
99 andreas 6534
 
6535
            // Skia has a bug and has changed the red and the blue color
6536
            // channel. Therefor we must change this 2 color channels for
6537
            // Linux based OSs here. On Android this is not necessary.
184 andreas 6538
//#ifdef __ANDROID__
100 andreas 6539
            *bpix = SkColorSetARGB(al, red, green, blue);
184 andreas 6540
//#else
6541
//            *bpix = SkColorSetARGB(al, blue, green, red);
6542
//#endif
99 andreas 6543
        }
6544
    }
6545
 
100 andreas 6546
    SkPaint paint;
6547
    paint.setBlendMode(SkBlendMode::kSrcOver);
6548
    SkCanvas can(Bm);
179 andreas 6549
    sk_sp<SkImage> _image = SkImage::MakeFromBitmap(base);
6550
    can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
99 andreas 6551
    return Bm;
6552
}
6553
 
161 andreas 6554
SkBitmap TButton::colorImage(SkBitmap& base, SkBitmap& alpha, SkColor col, SkColor bg, bool useBG)
80 andreas 6555
{
81 andreas 6556
    DECL_TRACER("TButton::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)");
80 andreas 6557
 
161 andreas 6558
    int width = base.info().width();
6559
    int height = base.info().height();
6560
 
80 andreas 6561
    if (width <= 0 || height <= 0)
6562
    {
169 andreas 6563
        MSG_WARNING("Got invalid width or height! (width: " << width << ", height: " << height << ")");
161 andreas 6564
        return SkBitmap();
80 andreas 6565
    }
6566
 
169 andreas 6567
    if (!alpha.empty())
161 andreas 6568
    {
169 andreas 6569
        if (width != alpha.info().width() || height != alpha.info().height())
6570
        {
6571
            MSG_ERROR("Base and alpha masks have different size!");
6572
            return SkBitmap();
6573
        }
161 andreas 6574
    }
6575
 
80 andreas 6576
    SkBitmap maskBm;
6577
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
160 andreas 6578
    maskBm.eraseColor(SK_ColorTRANSPARENT);
80 andreas 6579
 
6580
    for (int ix = 0; ix < width; ix++)
6581
    {
6582
        for (int iy = 0; iy < height; iy++)
6583
        {
169 andreas 6584
            SkColor pixelAlpha = 0;
6585
 
6586
            if (!alpha.empty())
6587
                pixelAlpha = alpha.getColor(ix, iy);
6588
            else
6589
                pixelAlpha = base.getColor(ix, iy);
6590
 
80 andreas 6591
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
6592
 
6593
            if (!wpix)
6594
            {
6595
                MSG_ERROR("No pixel buffer!");
6596
                break;
6597
            }
6598
 
169 andreas 6599
            uint32_t ala = SkColorGetA(pixelAlpha);
81 andreas 6600
 
169 andreas 6601
            if (ala == 0 && !useBG)
160 andreas 6602
                pixelAlpha = col;
169 andreas 6603
            else if (ala == 0)
160 andreas 6604
                pixelAlpha = bg;
81 andreas 6605
            else
6606
            {
161 andreas 6607
                // We've to change the red and the blue color channel because
6608
                // of an error in the Skia library.
159 andreas 6609
                uint32_t blue = SkColorGetR(col);
6610
                uint32_t green = SkColorGetG(col);
6611
                uint32_t red = SkColorGetB(col);
161 andreas 6612
 
169 andreas 6613
                if (alpha.empty())
6614
                {
6615
                    uint32_t pred = SkColorGetR(pixelAlpha);
6616
                    uint32_t pgreen = SkColorGetG(pixelAlpha);
6617
                    uint32_t pblue = SkColorGetB(pixelAlpha);
6618
                    uint32_t maxChan = SkColorGetG(SK_ColorWHITE);
6619
 
6620
                    red   = ((pred == maxChan) ? pred : red);
6621
                    green = ((pgreen == maxChan) ? pgreen : green);
6622
                    blue  = ((pblue == maxChan) ? pblue : blue);
6623
                }
6624
                else if (ala == 0)
160 andreas 6625
                    red = green = blue = 0;
6626
 
169 andreas 6627
                pixelAlpha = SkColorSetARGB(ala, red, green, blue);
81 andreas 6628
            }
6629
 
160 andreas 6630
            *wpix = pixelAlpha;
169 andreas 6631
//            MSG_DEBUG("Pixel " << ix << ", " << iy << ": #" << std::setw(8) << std::setfill('0') << std::hex << pixelAlpha);
80 andreas 6632
        }
6633
    }
6634
 
169 andreas 6635
    if (!alpha.empty())
6636
    {
6637
        SkPaint paint;
6638
        paint.setBlendMode(SkBlendMode::kSrcOver);
6639
        SkCanvas can(maskBm);
179 andreas 6640
        sk_sp<SkImage> _image = SkImage::MakeFromBitmap(base);
6641
        can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
169 andreas 6642
    }
6643
 
160 andreas 6644
    return maskBm;
80 andreas 6645
}
6646
 
161 andreas 6647
SkBitmap TButton::retrieveBorderImage(const string& pa, const string& pb, SkColor color, SkColor bgColor)
160 andreas 6648
{
161 andreas 6649
    DECL_TRACER("TButton::retrieveBorderImage(const string& pa, const string& pb, SkColor color, SkColor bgColor)");
160 andreas 6650
 
6651
    SkBitmap bm, bma;
6652
 
161 andreas 6653
    if (!pa.empty() && !retrieveImage(pa, &bm))
160 andreas 6654
        return SkBitmap();
6655
 
161 andreas 6656
    if (!pb.empty() && !retrieveImage(pb, &bma))
160 andreas 6657
        return SkBitmap();
6658
 
161 andreas 6659
    return colorImage(bm, bma, color, bgColor, false);
160 andreas 6660
}
6661
 
99 andreas 6662
bool TButton::retrieveImage(const string& path, SkBitmap* image)
6663
{
6664
    DECL_TRACER("TButton::retrieveImage(const string& path, SkBitmap* image)");
6665
 
6666
    sk_sp<SkData> im;
6667
 
6668
    if (!(im = readImage(path)))
6669
        return false;
6670
 
6671
    DecodeDataToBitmap(im, image);
6672
 
6673
    if (image->empty())
6674
    {
6675
        MSG_WARNING("Could not create the image " << path);
6676
        return false;
6677
    }
6678
 
6679
    return true;
6680
}
6681
 
6 andreas 6682
void TButton::show()
6683
{
6684
    DECL_TRACER("TButton::show()");
6685
 
15 andreas 6686
    visible = true;
6687
    makeElement();
6 andreas 6688
 
15 andreas 6689
    if (isSystemButton() && !mSystemReg)
6690
        registerSystemButton();
6691
}
6692
 
6693
void TButton::showLastButton()
6694
{
6695
    DECL_TRACER("TButton::showLastButton()");
6696
 
6697
    if (mLastImage.empty())
6698
        return;
6699
 
38 andreas 6700
    if (!_displayButton && gPageManager)
6701
        _displayButton = gPageManager->getCallbackDB();
6702
 
195 andreas 6703
    if (!prg_stopped && visible)
6 andreas 6704
    {
15 andreas 6705
        ulong parent = mHandle & 0xffff0000;
6706
        size_t rowBytes = mLastImage.info().minRowBytes();
26 andreas 6707
        int rwidth = wt;
6708
        int rheight = ht;
6709
        int rleft = lt;
6710
        int rtop = tp;
43 andreas 6711
#ifdef _SCALE_SKIA_
26 andreas 6712
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6713
        {
6714
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6715
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6716
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
6717
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
6718
        }
43 andreas 6719
#endif
195 andreas 6720
        if (type == TEXT_INPUT)
6721
        {
6722
            if (gPageManager && gPageManager->getCallbackInputText())
6723
            {
6724
                BITMAP_t bm;
6725
                bm.buffer = (unsigned char *)mLastImage.getPixels();
6726
                bm.rowBytes = rowBytes;
6727
                bm.left = rleft;
6728
                bm.top = rtop;
6729
                bm.width = rwidth;
6730
                bm.height = rheight;
6731
                gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
6732
            }
6733
        }
206 andreas 6734
        else if (type == LISTBOX)
6735
        {
6736
            if (gPageManager && gPageManager->getCallbackListBox())
6737
            {
6738
                BITMAP_t bm;
6739
                bm.buffer = (unsigned char *)mLastImage.getPixels();
6740
                bm.rowBytes = rowBytes;
6741
                bm.left = rleft;
6742
                bm.top = rtop;
6743
                bm.width = rwidth;
6744
                bm.height = rheight;
6745
                gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
6746
            }
6747
        }
195 andreas 6748
        else if (_displayButton)
6749
            _displayButton(mHandle, parent, (unsigned char *)mLastImage.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
6750
 
176 andreas 6751
        mChanged = false;
15 andreas 6752
    }
6753
}
6754
 
6755
void TButton::hide(bool total)
6756
{
6757
    DECL_TRACER("TButton::hide()");
6758
 
101 andreas 6759
//    if (type == MULTISTATE_GENERAL && ar == 1)
6760
//        mAniStop = true;
15 andreas 6761
 
21 andreas 6762
    if (!prg_stopped && total)
15 andreas 6763
    {
26 andreas 6764
        int rwidth = wt;
6765
        int rheight = ht;
6766
        int rleft = lt;
6767
        int rtop = tp;
6768
 
38 andreas 6769
        ulong parent = mHandle & 0xffff0000;
6770
        THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
6771
 
6772
        if (tr && tr->mImageRefresh)
6773
        {
6774
            if (tr->mImageRefresh->isRunning())
6775
                tr->mImageRefresh->stop();
6776
        }
43 andreas 6777
#ifdef _SCALE_SKIA_
26 andreas 6778
        if (gPageManager && gPageManager->getScaleFactor() != 1.0)
6779
        {
6780
            rwidth = (int)((double)wt * gPageManager->getScaleFactor());
6781
            rheight = (int)((double)ht * gPageManager->getScaleFactor());
6782
            rleft = (int)((double)lt * gPageManager->getScaleFactor());
6783
            rtop = (int)((double)tp * gPageManager->getScaleFactor());
6784
        }
43 andreas 6785
#endif
190 andreas 6786
        if (type == TEXT_INPUT)
6787
        {
6788
            if (gPageManager && gPageManager->getCallDropButton())
6789
                gPageManager->getCallDropButton()(mHandle);
6790
 
6791
            visible = false;
6792
            return;
6793
        }
6794
 
28 andreas 6795
        SkBitmap imgButton;
165 andreas 6796
        size_t rowBytes = 0;
28 andreas 6797
 
165 andreas 6798
        if (rwidth < 0 || rheight < 0)
6799
        {
6800
            MSG_ERROR("Invalid size of image: " << rwidth << " x " << rheight);
6801
            return;
6802
        }
6803
 
6804
        try
6805
        {
6806
            imgButton.allocN32Pixels(rwidth, rheight);
6807
            imgButton.eraseColor(SK_ColorTRANSPARENT);
6808
            rowBytes = imgButton.info().minRowBytes();
6809
        }
6810
        catch (std::exception& e)
6811
        {
6812
            MSG_ERROR("Error creating image: " << e.what());
6813
            visible = false;
6814
            return;
6815
        }
6816
 
38 andreas 6817
        if (!_displayButton && gPageManager)
6818
            _displayButton = gPageManager->getCallbackDB();
6819
 
6820
        if (_displayButton)
176 andreas 6821
        {
38 andreas 6822
            _displayButton(mHandle, parent, (unsigned char *)imgButton.getPixels(), rwidth, rheight, rowBytes, rleft, rtop);
176 andreas 6823
            mChanged = false;
6824
        }
15 andreas 6825
    }
6826
 
6827
    visible = false;
6828
}
6829
 
154 andreas 6830
bool TButton::isClickable(int x, int y)
15 andreas 6831
{
6832
    DECL_TRACER("TButton::isClickable()");
6833
 
154 andreas 6834
    if (mEnabled && /*((cp != 0 && ch != 0) || (lp != 0 && lv != 0) || !cm.empty() || !op.empty() || !pushFunc.empty() || isSystemButton()) &&*/ hs.compare("passThru") != 0)
6835
    {
6836
        if (x != -1 && y != -1 && hs.empty() && !mLastImage.empty() && isPixelTransparent(x, y))
6837
            return false;
150 andreas 6838
 
15 andreas 6839
        return true;
154 andreas 6840
    }
15 andreas 6841
 
6842
    return false;
6843
}
6844
 
6845
/**
6846
 * Handling of system button "connection state". It consists of 12 states
6847
 * indicating the network status. The states have the following meaning:
6848
 *
6849
 * 0      Diconnected (never was connected before since startup)
6850
 * 1 - 6  Connected (blink may be shown with dark and light green)
6851
 * 7, 8   Disconnected (timeout or loss of connection)
6852
 * 9 - 11 Connection in progress
6853
 */
6854
void TButton::funcNetwork(int state)
6855
{
6856
    mutex_sysdraw.lock();
6857
    DECL_TRACER("TButton::funcNetwork(int state)");
6858
 
6859
    mLastLevel = state;
6860
    mActInstance = state;
176 andreas 6861
    mChanged = true;
15 andreas 6862
 
6863
    if (visible)
6864
        makeElement(state);
6865
 
6866
    mutex_sysdraw.unlock();
6867
}
6868
 
6869
/**
6870
 * Handling the timer event from the controller. This comes usualy every
6871
 * 20th part of a second (1 second / 20)
6872
 */
6873
void TButton::funcTimer(const amx::ANET_BLINK& blink)
6874
{
6875
    mutex_sysdraw.lock();
6876
    DECL_TRACER("TButton::funcTimer(const amx::ANET_BLINK& blink)");
6877
 
6878
    string tm;
6879
    std::stringstream sstr;
6880
 
6881
    switch (ad)
6882
    {
6883
        case 141:   // Standard time
6884
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
6885
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << ":"
6886
                 << std::setw(2) << std::setfill('0') << (int)blink.second;
6887
            mLastBlink = blink;
6888
        break;
6889
 
6890
        case 142:   // Time AM/PM
6 andreas 6891
        {
15 andreas 6892
            int hour = (blink.hour > 12) ? (blink.hour - 12) : blink.hour;
6893
            sstr << std::setw(2) << std::setfill('0') << hour << ":"
6894
                 << std::setw(2) << std::setfill('0') << (int)blink.minute << " ";
6895
 
6896
            if (blink.hour <= 12)
6897
                sstr << "AM";
6898
            else
6899
                sstr << "PM";
6900
 
6901
            mLastBlink = blink;
6 andreas 6902
        }
15 andreas 6903
        break;
6 andreas 6904
 
15 andreas 6905
        case 143:   // Time 24 hours
6906
            sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
6907
                 << std::setw(2) << std::setfill('0') << (int)blink.minute;
6908
            mLastBlink = blink;
6909
        break;
6910
 
6911
        case 151:   // Weekday
6912
            switch (blink.weekday)
6913
            {
6914
                case 0: sstr << "Monday"; break;
6915
                case 1: sstr << "Tuesday"; break;
6916
                case 2: sstr << "Wednesday"; break;
6917
                case 3: sstr << "Thursday"; break;
6918
                case 4: sstr << "Friday"; break;
6919
                case 5: sstr << "Saturday"; break;
6920
                case 6: sstr << "Sunday"; break;
6921
            }
6922
        break;
6923
 
6924
        case 152:   // Date mm/dd
6925
            sstr << (int)blink.month << "/" << (int)blink.day;
6926
        break;
6927
 
6928
        case 153:   // Date dd/mm
6929
            sstr << (int)blink.day << "/" << (int)blink.month;
6930
        break;
6931
 
6932
        case 154:   // Date mm/dd/yyyy
6933
            sstr << (int)blink.month << "/" << (int)blink.day << "/" << (int)blink.year;
6934
        break;
6935
 
6936
        case 155:   // Date dd/mm/yyyy
6937
            sstr << blink.day << "/" << blink.month << "/" << blink.year;
6938
        break;
6939
 
6940
        case 156:   // Date month dd/yyyy
6941
            switch (blink.month)
6942
            {
6943
                case 1:  sstr << "January"; break;
6944
                case 2:  sstr << "February"; break;
6945
                case 3:  sstr << "March"; break;
6946
                case 4:  sstr << "April"; break;
6947
                case 5:  sstr << "May"; break;
6948
                case 6:  sstr << "June"; break;
6949
                case 7:  sstr << "July"; break;
6950
                case 8:  sstr << "August"; break;
6951
                case 9:  sstr << "September"; break;
6952
                case 10:  sstr << "October"; break;
6953
                case 11:  sstr << "November"; break;
6954
                case 12:  sstr << "December"; break;
6955
            }
6956
 
6957
            sstr << " " << (int)blink.day << "/" << (int)blink.year;
6958
        break;
6959
 
6960
        case 157:   // Date dd month yyyy
6961
            sstr << (int)blink.day;
6962
 
6963
            switch (blink.month)
6964
            {
6965
                case 1:  sstr << "January"; break;
6966
                case 2:  sstr << "February"; break;
6967
                case 3:  sstr << "March"; break;
6968
                case 4:  sstr << "April"; break;
6969
                case 5:  sstr << "May"; break;
6970
                case 6:  sstr << "June"; break;
6971
                case 7:  sstr << "July"; break;
6972
                case 8:  sstr << "August"; break;
6973
                case 9:  sstr << "September"; break;
6974
                case 10:  sstr << "October"; break;
6975
                case 11:  sstr << "November"; break;
6976
                case 12:  sstr << "December"; break;
6977
            }
6978
 
6979
            sstr << " " << (int)blink.year;
6980
        break;
6981
 
6982
        case 158:   // Date yyyy-mm-dd
6983
            sstr << (int)blink.year << "-" << (int)blink.month << "-" << (int)blink.day;
6984
        break;
176 andreas 6985
 
6986
        default:
6987
            mutex_sysdraw.unlock();
6988
            return;
6 andreas 6989
    }
15 andreas 6990
 
6991
    vector<SR_T>::iterator iter;
6992
    tm = sstr.str();
6993
 
118 andreas 6994
    for (iter = sr.begin(); iter != sr.end(); ++iter)
15 andreas 6995
        iter->te = tm;
6996
 
176 andreas 6997
    mChanged = true;
6998
 
15 andreas 6999
    if (visible)
7000
        makeElement(mActInstance);
7001
 
7002
    mutex_sysdraw.unlock();
6 andreas 7003
}
7 andreas 7004
 
15 andreas 7005
bool TButton::isPixelTransparent(int x, int y)
7006
{
7007
    DECL_TRACER("TButton::isPixelTransparent(int x, int y)");
7008
 
54 andreas 7009
    // If there is no image we treat it as a non transpararent pixel.
7010
    if (sr[mActInstance].mi.empty() && sr[mActInstance].bm.empty())
7011
        return false;
7012
 
7013
    // The mLastImage must never be empty! Although this should never be true,
7014
    // we treat it as a transparent pixel if it happen.
15 andreas 7015
    if (mLastImage.empty())
7016
    {
7017
        MSG_ERROR("Internal error: No image for button available!");
7018
        return true;
7019
    }
7020
 
54 andreas 7021
    // Make sure the coordinates are inside the bounds. A test for a pixel
7022
    // outside the bounds would lead in an immediate exit because of an assert
7023
    // test in skia. Although this should not happen, we treat the pixel as
7024
    // transparent in this case, because the coordinates didn't hit the button.
31 andreas 7025
    if (x < 0 || x >= mLastImage.info().width() || y < 0 || y >= mLastImage.info().height())
7026
    {
7027
        MSG_ERROR("The X or Y coordinate is out of bounds!");
7028
        MSG_ERROR("X=" << x << ", Y=" << y << ", width=" << mLastImage.info().width() << ", height=" << mLastImage.info().height());
54 andreas 7029
        return true;
31 andreas 7030
    }
7031
 
54 andreas 7032
    float alpha = mLastImage.getAlphaf(x, y);   // Get the alpha value (0.0 to 1.0)
15 andreas 7033
 
7034
    if (alpha != 0.0)
7035
        return false;
7036
 
7037
    return true;
7038
}
7039
 
71 andreas 7040
bool TButton::checkForSound()
7041
{
7042
    DECL_TRACER("TButton::checkForSound()");
7043
 
7044
    vector<SR_T>::iterator iter;
7045
 
7046
    for (iter = sr.begin(); iter != sr.end(); ++iter)
7047
    {
7048
        if (!iter->sd.empty())
7049
            return true;
7050
    }
7051
 
7052
    return false;
7053
}
7054
 
79 andreas 7055
bool TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)
7056
{
7057
    DECL_TRACER("TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)");
7058
 
7059
    if (!bm)
7060
        return false;
7061
 
7062
    if (scaleWidth == 1.0 && scaleHeight == 1.0)
7063
        return true;
7064
 
7065
    SkPaint paint;
7066
    paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 7067
//    paint.setFilterQuality(kHigh_SkFilterQuality);
79 andreas 7068
    // Calculate new dimension
7069
    SkImageInfo info = bm->info();
7070
    int width  = std::max(1, (int)((double)info.width() * scaleWidth));
7071
    int height = std::max(1, (int)((double)info.height() * scaleHeight));
80 andreas 7072
    MSG_DEBUG("Scaling image to size " << width << " x " << height);
79 andreas 7073
    // Create a canvas and draw new image
7074
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
7075
    bm->allocN32Pixels(width, height);
7076
    bm->eraseColor(SK_ColorTRANSPARENT);
7077
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
7078
    SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
179 andreas 7079
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
79 andreas 7080
    return true;
7081
}
7082
 
7083
bool TButton::stretchImageWidth(SkBitmap *bm, int width)
7084
{
7085
    DECL_TRACER("TButton::stretchImageWidth(SkBitmap *bm, int width)");
7086
 
80 andreas 7087
    if (!bm)
7088
        return false;
7089
 
157 andreas 7090
    int rwidth = width;
79 andreas 7091
    SkPaint paint;
7092
    paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 7093
//    paint.setFilterQuality(kHigh_SkFilterQuality);
79 andreas 7094
 
7095
    SkImageInfo info = bm->info();
7096
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
157 andreas 7097
 
7098
    if (width <= 0)
7099
        rwidth = info.width() + width;
7100
 
7101
    if (rwidth <= 0)
7102
        rwidth = 1;
7103
 
7104
    MSG_DEBUG("Width: " << rwidth << ", Height: " << info.height());
7105
    bm->allocN32Pixels(rwidth, info.height());
79 andreas 7106
    bm->eraseColor(SK_ColorTRANSPARENT);
7107
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
157 andreas 7108
    SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, info.height());
179 andreas 7109
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
80 andreas 7110
    return true;
79 andreas 7111
}
7112
 
7113
bool TButton::stretchImageHeight(SkBitmap *bm, int height)
7114
{
7115
    DECL_TRACER("TButton::stretchImageHeight(SkBitmap *bm, int height)");
7116
 
80 andreas 7117
    if (!bm)
7118
        return false;
7119
 
157 andreas 7120
    int rheight = height;
79 andreas 7121
    SkPaint paint;
7122
    paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 7123
//    paint.setFilterQuality(kHigh_SkFilterQuality);
79 andreas 7124
 
7125
    SkImageInfo info = bm->info();
157 andreas 7126
 
7127
    if (height <= 0)
7128
        rheight = info.height() + height;
7129
 
7130
    if (rheight <= 0)
7131
        rheight = 1;
7132
 
79 andreas 7133
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
157 andreas 7134
    MSG_DEBUG("Width: " << info.width() << ", Height: " << rheight);
7135
    bm->allocN32Pixels(info.width(), rheight);
79 andreas 7136
    bm->eraseColor(SK_ColorTRANSPARENT);
7137
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
157 andreas 7138
    SkRect rect = SkRect::MakeXYWH(0, 0, info.width(), rheight);
179 andreas 7139
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
80 andreas 7140
    return true;
79 andreas 7141
}
7142
 
157 andreas 7143
bool TButton::stretchImageWH(SkBitmap *bm, int width, int height)
7144
{
7145
    DECL_TRACER("TButton::stretchImageWH(SkBitmap *bm, int width, int height)");
7146
 
7147
    if (!bm)
7148
        return false;
7149
 
7150
    int rwidth = width;
7151
    int rheight = height;
7152
    SkPaint paint;
7153
    paint.setBlendMode(SkBlendMode::kSrc);
179 andreas 7154
//    paint.setFilterQuality(kHigh_SkFilterQuality);
157 andreas 7155
 
7156
    SkImageInfo info = bm->info();
7157
 
7158
    if (width <= 0)
7159
        rwidth = info.width() + width;
7160
 
7161
    if (height <= 0)
7162
        rheight = info.height() + height;
7163
 
7164
    if (rheight <= 0)
7165
        rheight = 1;
7166
 
7167
    if (rwidth <= 0)
7168
        rwidth = 1;
7169
 
7170
    sk_sp<SkImage> im = SkImage::MakeFromBitmap(*bm);
7171
    MSG_DEBUG("Width: " << rwidth << ", Height: " << rheight);
7172
    bm->allocN32Pixels(rwidth, rheight);
7173
    bm->eraseColor(SK_ColorTRANSPARENT);
7174
    SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
7175
    SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, rheight);
179 andreas 7176
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
157 andreas 7177
    return true;
7178
}
7179
 
15 andreas 7180
/**
7181
 * This button got the click because it matches the coordinates of a mouse
7182
 * click. It checkes whether it is clickable or not. If it is clickable, it
7183
 * depends on the type of element what happens.
7184
 */
7185
bool TButton::doClick(int x, int y, bool pressed)
7186
{
146 andreas 7187
    DECL_TRACER("TButton::doClick(int x, int y, bool pressed)");
154 andreas 7188
 
7189
    if (!isClickable(x, y))
7190
        return false;
7191
 
14 andreas 7192
    amx::ANET_SEND scmd;
15 andreas 7193
    int instance = 0;
31 andreas 7194
    int sx = x, sy = y;
71 andreas 7195
    bool isSystem = isSystemButton();
11 andreas 7196
 
71 andreas 7197
    if (gPageManager && !checkForSound() && (ch > 0 || lv > 0 || !pushFunc.empty() || isSystem))
7198
    {
7199
        TSystemSound sysSound(TConfig::getSystemPath(TConfig::SOUNDS));
7200
 
7201
        if (pressed && gPageManager->havePlaySound() && sysSound.getSystemSoundState())
7202
            gPageManager->getCallPlaySound()(sysSound.getTouchFeedbackSound());
7203
    }
7204
 
43 andreas 7205
#ifdef _SCALE_SKIA_
31 andreas 7206
    // To be able to test a button for a transparent pixel, we must scale
7207
    // the coordinates because the test is made on the last drawn image and
7208
    // this image is scaled (if scaling is activated).
7209
    if (TConfig::getScale() && gPageManager && gPageManager->getScaleFactor() != 1.0)
7210
    {
7211
        double scaleFactor = gPageManager->getScaleFactor();
7212
        sx = (int)((double)x * scaleFactor);
7213
        sy = (int)((double)y * scaleFactor);
7214
    }
43 andreas 7215
#endif
11 andreas 7216
    if (type == GENERAL)
7217
    {
206 andreas 7218
        MSG_DEBUG("Button type: GENERAL; System button: " << (isSystem ? "YES" : "NO") << "; CH: " << cp << ":" << ch << "; AD: " << ap << ":" << ad);
35 andreas 7219
 
206 andreas 7220
        if (isSystem && ch == SYSTEM_ITEM_SOUNDSWITCH)   // Button sounds on/off
11 andreas 7221
        {
7222
            if (pressed)
71 andreas 7223
            {
7224
                MSG_TRACE("System button sounds are toggled ...");
195 andreas 7225
                TConfig::setTemporary(false);
71 andreas 7226
                bool sstate = TConfig::getSystemSoundState();
7227
 
7228
                if (sstate)
7229
                    mActInstance = instance = 0;
7230
                else
7231
                    mActInstance = instance = 1;
7232
 
7233
                TConfig::saveSystemSoundState(!sstate);
7234
                TConfig::saveSettings();
176 andreas 7235
                mChanged = true;
198 andreas 7236
                drawButton(mActInstance, true);
71 andreas 7237
            }
7238
        }
206 andreas 7239
        else if (isSystem && ch == SYSTEM_ITEM_SETUPPAGE)  // Enter setup page
113 andreas 7240
        {
7241
            if (pressed)
7242
            {
7243
                if (gPageManager && gPageManager->haveSetupPage())
7244
                    gPageManager->callSetupPage();
7245
            }
7246
        }
206 andreas 7247
        else if (isSystem && ch == SYSTEM_ITEM_SHUTDOWN)  // Shutdown program
113 andreas 7248
        {
7249
            if (pressed)
7250
            {
7251
                if (gPageManager && gPageManager->haveShutdown())
7252
                    gPageManager->callShutdown();
7253
            }
7254
        }
206 andreas 7255
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEUP)     // System volume up
154 andreas 7256
        {
195 andreas 7257
            TConfig::setTemporary(true);
154 andreas 7258
            int vol = TConfig::getSystemVolume() + 10;
7259
 
7260
            if (vol > 100)
7261
                vol = 100;
7262
 
198 andreas 7263
            if (pressed)
7264
                TConfig::saveSystemVolume(vol);
154 andreas 7265
 
7266
            if (pressed)
7267
                mActInstance = instance = 1;
7268
            else
7269
                mActInstance = instance = 0;
7270
 
176 andreas 7271
            mChanged = true;
154 andreas 7272
            drawButton(mActInstance, true);
7273
 
198 andreas 7274
            if (pressed && gPageManager)
154 andreas 7275
            {
198 andreas 7276
                int channel = TConfig::getChannel();
7277
                int system = TConfig::getSystem();
7278
 
154 andreas 7279
                amx::ANET_COMMAND cmd;
7280
                cmd.MC = 0x000a;
7281
                cmd.device1 = channel;
163 andreas 7282
                cmd.port1 = 0;
154 andreas 7283
                cmd.system = system;
7284
                cmd.data.message_value.system = system;
7285
                cmd.data.message_value.value = 9;   // System volume
7286
                cmd.data.message_value.content.integer = vol;
7287
                cmd.data.message_value.device = channel;
163 andreas 7288
                cmd.data.message_value.port = 0;    // Must be the address port of button
154 andreas 7289
                cmd.data.message_value.type = 0x20; // Unsigned int
7290
                gPageManager->doCommand(cmd);
7291
            }
7292
        }
206 andreas 7293
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEDOWN)     // System volume down
154 andreas 7294
        {
195 andreas 7295
            TConfig::setTemporary(true);
154 andreas 7296
            int vol = TConfig::getSystemVolume() - 10;
7297
 
7298
            if (vol < 0)
7299
                vol = 0;
7300
 
198 andreas 7301
            if (pressed)
7302
                TConfig::saveSystemVolume(vol);
154 andreas 7303
 
7304
            if (pressed)
7305
                mActInstance = instance = 1;
7306
            else
7307
                mActInstance = instance = 0;
7308
 
176 andreas 7309
            mChanged = true;
154 andreas 7310
            drawButton(mActInstance, true);
7311
 
198 andreas 7312
            if (pressed && gPageManager)
154 andreas 7313
            {
198 andreas 7314
                int channel = TConfig::getChannel();
7315
                int system = TConfig::getSystem();
7316
 
154 andreas 7317
                amx::ANET_COMMAND cmd;
7318
                cmd.MC = 0x000a;
7319
                cmd.device1 = channel;
163 andreas 7320
                cmd.port1 = 0;
154 andreas 7321
                cmd.system = system;
7322
                cmd.data.message_value.system = system;
7323
                cmd.data.message_value.value = 9;   // System volume
7324
                cmd.data.message_value.content.integer = vol;
7325
                cmd.data.message_value.device = channel;
163 andreas 7326
                cmd.data.message_value.port = 0;    // Must be the address port of button
154 andreas 7327
                cmd.data.message_value.type = 0x20; // Unsigned int
7328
                gPageManager->doCommand(cmd);
7329
            }
7330
        }
206 andreas 7331
        else if (isSystem && ch == SYSTEM_ITEM_VOLUMEMUTE)     // System mute
141 andreas 7332
        {
7333
            if (pressed)
7334
            {
195 andreas 7335
                TConfig::setTemporary(true);
141 andreas 7336
                bool mute = TConfig::getMuteState();
7337
 
7338
                if (mute)
7339
                    mActInstance = instance = 0;
7340
                else
7341
                    mActInstance = instance = 1;
7342
 
7343
                TConfig::setMuteState(!mute);
7344
 
7345
                if (gPageManager && gPageManager->getCallMuteSound())
7346
                    gPageManager->getCallMuteSound()(!mute);
7347
 
176 andreas 7348
                mChanged = true;
195 andreas 7349
                drawButton(mActInstance, true);
141 andreas 7350
            }
7351
        }
206 andreas 7352
        else if (isSystem && ch == SYSTEM_ITEM_BTSAVESETTINGS)     // System button OK: Save settings
192 andreas 7353
        {
7354
            if (pressed)
7355
            {
7356
                mActInstance = instance = 1;
7357
                TConfig::setTemporary(true);
7358
                TConfig::saveSettings();
198 andreas 7359
                drawButton(mActInstance, true);
192 andreas 7360
 
217 andreas 7361
                if (gPageManager && gPageManager->getSettings() != gPageManager->getSystemSettings())
197 andreas 7362
                    gPageManager->hideSetup();
217 andreas 7363
                else if (gPageManager && gPageManager->getDisplayMessage())
7364
                    gPageManager->getDisplayMessage()("Settings were saved!", "Info");
7365
                else
7366
                    MSG_INFO("Settings were saved.");
192 andreas 7367
            }
7368
            else
7369
            {
7370
                mActInstance = instance = 0;
198 andreas 7371
                drawButton(mActInstance, true);
192 andreas 7372
            }
7373
        }
206 andreas 7374
        else if (isSystem && ch == SYSTEM_ITEM_BTCANCELSETTINGS)     // System button Cancel: Cancel settings changes
192 andreas 7375
        {
7376
            if (pressed)
7377
            {
7378
                mActInstance = instance = 1;
7379
                TConfig::reset();
198 andreas 7380
                drawButton(mActInstance, true);
192 andreas 7381
 
217 andreas 7382
                if (gPageManager && gPageManager->getSettings() != gPageManager->getSystemSettings())
197 andreas 7383
                    gPageManager->hideSetup();
192 andreas 7384
            }
7385
            else
7386
            {
7387
                mActInstance = instance = 0;
198 andreas 7388
                drawButton(mActInstance, true);
192 andreas 7389
            }
7390
        }
206 andreas 7391
        else if (isSystem && ch == SYSTEM_ITEM_SIPENABLE)     // SIP: enabled/disabled
198 andreas 7392
        {
7393
            if (pressed)
7394
            {
7395
                TConfig::setTemporary(true);
7396
                bool st = TConfig::getSIPstatus();
7397
                mActInstance = instance = (st ? 0 : 1);
7398
                mChanged = true;
7399
                TConfig::setSIPstatus(!st);
7400
                drawButton(mActInstance, true);
7401
            }
7402
        }
206 andreas 7403
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGINFO)    // Debug info
192 andreas 7404
        {
7405
            if (pressed)
7406
            {
7407
                TConfig::setTemporary(true);
7408
                uint ll = TConfig::getLogLevelBits();
7409
                bool st = (ll & HLOG_INFO) ? true : false;
7410
                mActInstance = instance = (st ? 0 : 1);
7411
                ll = (st ? (ll &= RLOG_INFO) : (ll |= HLOG_INFO));
7412
                mChanged = true;
7413
                TConfig::saveLogLevel(ll);
195 andreas 7414
                drawButton(mActInstance, true);
192 andreas 7415
            }
7416
        }
206 andreas 7417
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGWARNING)    // Debug warning
192 andreas 7418
        {
7419
            if (pressed)
7420
            {
7421
                TConfig::setTemporary(true);
7422
                uint ll = TConfig::getLogLevelBits();
7423
                bool st = (ll & HLOG_WARNING) ? true : false;
7424
                mActInstance = instance = (st ? 0 : 1);
7425
                ll = (st ? (ll &= RLOG_WARNING) : (ll |= HLOG_WARNING));
7426
                mChanged = true;
7427
                TConfig::saveLogLevel(ll);
195 andreas 7428
                drawButton(mActInstance, true);
192 andreas 7429
            }
7430
        }
206 andreas 7431
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGERROR)    // Debug error
192 andreas 7432
        {
7433
            if (pressed)
7434
            {
7435
                TConfig::setTemporary(true);
7436
                uint ll = TConfig::getLogLevelBits();
7437
                bool st = (ll & HLOG_ERROR) ? true : false;
7438
                mActInstance = instance = (st ? 0 : 1);
7439
                ll = (st ? (ll &= RLOG_ERROR) : (ll |= HLOG_ERROR));
7440
                mChanged = true;
7441
                TConfig::saveLogLevel(ll);
195 andreas 7442
                drawButton(mActInstance, true);
192 andreas 7443
            }
7444
        }
206 andreas 7445
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGTRACE)    // Debug trace
192 andreas 7446
        {
7447
            if (pressed)
7448
            {
7449
                TConfig::setTemporary(true);
7450
                uint ll = TConfig::getLogLevelBits();
7451
                bool st = (ll & HLOG_TRACE) ? true : false;
7452
                mActInstance = instance = (st ? 0 : 1);
7453
                ll = (st ? (ll &= RLOG_TRACE) : (ll |= HLOG_TRACE));
7454
                mChanged = true;
7455
                TConfig::saveLogLevel(ll);
195 andreas 7456
                drawButton(mActInstance, true);
192 andreas 7457
            }
7458
        }
206 andreas 7459
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGDEBUG)    // Debug debug
192 andreas 7460
        {
7461
            if (pressed)
7462
            {
7463
                TConfig::setTemporary(true);
7464
                uint ll = TConfig::getLogLevelBits();
7465
                bool st = (ll & HLOG_DEBUG) ? true : false;
7466
                mActInstance = instance = (st ? 0 : 1);
7467
                ll = (st ? (ll &= RLOG_DEBUG) : (ll |= HLOG_DEBUG));
7468
                mChanged = true;
7469
                TConfig::saveLogLevel(ll);
195 andreas 7470
                drawButton(mActInstance, true);
192 andreas 7471
            }
7472
        }
206 andreas 7473
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROTOCOL)    // Debug protocol
192 andreas 7474
        {
7475
            if (pressed)
7476
            {
7477
                TConfig::setTemporary(true);
7478
                uint ll = TConfig::getLogLevelBits();
7479
                bool st = (ll & HLOG_PROTOCOL) == HLOG_PROTOCOL ? true : false;
7480
                mActInstance = instance = (st ? 0 : 1);
7481
                ll = (st ? (ll &= RLOG_PROTOCOL) : (ll |= HLOG_PROTOCOL));
7482
                mChanged = true;
7483
                TConfig::saveLogLevel(ll);
195 andreas 7484
                drawButton(mActInstance, true);
192 andreas 7485
 
7486
                if (gPageManager)
7487
                    gPageManager->updateActualPage();
7488
            }
7489
        }
206 andreas 7490
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGALL)    // Debug all
192 andreas 7491
        {
7492
            if (pressed)
7493
            {
7494
                TConfig::setTemporary(true);
7495
                uint ll = TConfig::getLogLevelBits();
7496
                bool st = (ll & HLOG_ALL) == HLOG_ALL ? true : false;
7497
                mActInstance = instance = (st ? 0 : 1);
7498
                ll = (st ? (ll &= RLOG_ALL) : (ll |= HLOG_ALL));
7499
                mChanged = true;
7500
                TConfig::saveLogLevel(ll);
195 andreas 7501
                drawButton(mActInstance, true);
192 andreas 7502
 
7503
                if (gPageManager)
7504
                    gPageManager->updateActualPage();
7505
            }
7506
        }
206 andreas 7507
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROFILE)    // Log profiling
192 andreas 7508
        {
7509
            if (pressed)
7510
            {
7511
                TConfig::setTemporary(true);
7512
                bool st = TConfig::getProfiling();
7513
                mActInstance = instance = (st ? 0 : 1);
7514
                mChanged = true;
7515
                TConfig::saveProfiling(!st);
195 andreas 7516
                drawButton(mActInstance, true);
192 andreas 7517
            }
7518
        }
206 andreas 7519
        else if (isSystem && ch == SYSTEM_ITEM_DEBUGLONG)    // Log long format
192 andreas 7520
        {
7521
            if (pressed)
7522
            {
7523
                TConfig::setTemporary(true);
7524
                bool st = TConfig::isLongFormat();
7525
                mActInstance = instance = (st ? 0 : 1);
7526
                mChanged = true;
7527
                TConfig::saveFormat(!st);
195 andreas 7528
                drawButton(mActInstance, true);
192 andreas 7529
            }
7530
        }
208 andreas 7531
        else if (isSystem && ch == SYSTEM_ITEM_LOGRESET)    // Log reset path
7532
        {
209 andreas 7533
            if (pressed)
208 andreas 7534
            {
209 andreas 7535
                char *HOME = getenv("HOME");
7536
                string logFile = TConfig::getLogFile();
208 andreas 7537
 
209 andreas 7538
                if (HOME)
7539
                {
7540
                    logFile = HOME;
7541
                    logFile += "/tpanel/tpanel.log";
7542
                }
208 andreas 7543
 
209 andreas 7544
                ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
7545
                TConfig::setTemporary(true);
7546
                TConfig::saveLogFile(logFile);
7547
                MSG_DEBUG("Setting text \"" << logFile << "\" to button " << TObject::handleToString(handle));
7548
 
7549
                if (gPageManager)
7550
                    gPageManager->setTextToButton(handle, logFile, true);
7551
            }
208 andreas 7552
        }
7553
        else if (isSystem && ch == SYSTEM_ITEM_LOGFILEOPEN) // Log file dialog
7554
        {
209 andreas 7555
            if (pressed && gPageManager && gPageManager->getFileDialogFunction())
7556
            {
7557
                TConfig::setTemporary(true);
7558
                ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
7559
                string currFile = TConfig::getLogFile();
7560
                gPageManager->getFileDialogFunction()(handle, currFile, "*.log *.txt", "log");
7561
            }
208 andreas 7562
        }
206 andreas 7563
        else if (isSystem && ch == SYSTEM_ITEM_FTPDOWNLOAD)    // FTP download surface button
195 andreas 7564
        {
7565
            if (pressed)
7566
            {
206 andreas 7567
                TConfig::setTemporary(false);
7568
                string surfaceOld = TConfig::getFtpSurface();
7569
                TConfig::setTemporary(true);
7570
                string surfaceNew = TConfig::getFtpSurface();
7571
 
7572
                MSG_DEBUG("Surface difference: Old: " << surfaceOld << ", New: " << surfaceNew);
7573
 
7574
                if (gPageManager && gPageManager->getDownloadSurface())
7575
                {
7576
                    size_t size = gPageManager->getFtpSurfaceSize(surfaceNew);
208 andreas 7577
                    gPageManager->getDownloadSurface()(surfaceNew, size);
206 andreas 7578
                }
7579
            }
7580
        }
7581
        else if (isSystem && ch == SYSTEM_ITEM_FTPPASSIVE)    // FTP passive mode
7582
        {
7583
            if (pressed)
7584
            {
7585
                TConfig::setTemporary(true);
195 andreas 7586
                bool st = TConfig::getFtpPassive();
7587
                mActInstance = instance = (st ? 0 : 1);
7588
                mChanged = true;
7589
                TConfig::saveFtpPassive(!st);
7590
                drawButton(mActInstance, true);
7591
            }
7592
        }
206 andreas 7593
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYSYSSOUND)    // Play system sound
195 andreas 7594
        {
7595
            if (pressed)
7596
            {
7597
                TConfig::setTemporary(true);
223 andreas 7598
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSystemSound();
195 andreas 7599
 
223 andreas 7600
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
195 andreas 7601
                    gPageManager->getCallPlaySound()(sound);
7602
            }
7603
        }
206 andreas 7604
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYBEEP)    // Play single beep
195 andreas 7605
        {
7606
            if (pressed)
7607
            {
7608
                TConfig::setTemporary(true);
223 andreas 7609
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSingleBeepSound();
195 andreas 7610
 
7611
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
7612
                    gPageManager->getCallPlaySound()(sound);
7613
            }
7614
        }
206 andreas 7615
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYDBEEP)    // Play double beep
195 andreas 7616
        {
7617
            if (pressed)
7618
            {
7619
                TConfig::setTemporary(true);
223 andreas 7620
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getDoubleBeepSound();
195 andreas 7621
 
7622
                if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
7623
                    gPageManager->getCallPlaySound()(sound);
7624
            }
7625
        }
223 andreas 7626
        else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYTESTSOUND)    // Play test sound
7627
        {
7628
            if (pressed)
7629
            {
7630
                TConfig::setTemporary(true);
7631
                string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/audioTest.wav";
7632
 
7633
                if (gPageManager && gPageManager->getCallPlaySound())
7634
                    gPageManager->getCallPlaySound()(sound);
7635
            }
7636
        }
206 andreas 7637
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPV4)    // SIP: IPv4
198 andreas 7638
        {
7639
            if (pressed)
7640
            {
7641
                TConfig::setTemporary(true);
7642
                bool st = TConfig::getSIPnetworkIPv4();
7643
                mActInstance = instance = (st ? 0 : 1);
7644
                mChanged = true;
7645
                TConfig::setSIPnetworkIPv4(!st);
7646
                drawButton(mActInstance, true);
7647
            }
7648
        }
206 andreas 7649
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPV6)    // SIP: IPv6
198 andreas 7650
        {
7651
            if (pressed)
7652
            {
7653
                TConfig::setTemporary(true);
7654
                bool st = TConfig::getSIPnetworkIPv6();
7655
                mActInstance = instance = (st ? 0 : 1);
7656
                mChanged = true;
7657
                TConfig::setSIPnetworkIPv6(!st);
7658
                drawButton(mActInstance, true);
7659
            }
7660
        }
206 andreas 7661
        else if (isSystem && ch == SYSTEM_ITEM_SIPIPHONE)    // SIP: internal phone
198 andreas 7662
        {
7663
            if (pressed)
7664
            {
7665
                TConfig::setTemporary(true);
7666
                bool st = TConfig::getSIPiphone();
7667
                mActInstance = instance = (st ? 0 : 1);
7668
                mChanged = true;
7669
                TConfig::setSIPiphone(!st);
7670
                drawButton(mActInstance, true);
7671
            }
7672
        }
195 andreas 7673
#ifdef __ANDROID__
206 andreas 7674
        else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT)    // Scale to fit
195 andreas 7675
        {
7676
            if (pressed)
7677
            {
7678
                TConfig::setTemporary(true);
7679
                bool st = TConfig::getScale();
7680
                mActInstance = instance = (st ? 0 : 1);
7681
                mChanged = true;
7682
                TConfig::saveScale(!st);
7683
                drawButton(mActInstance, true);
7684
            }
7685
        }
206 andreas 7686
        else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER)    // Show banner (disabled)
195 andreas 7687
        {
7688
            if (sr[0].oo < 0)
7689
            {
7690
                sr[0].oo = 128;
7691
                mChanged = true;
7692
                mActInstance = 0;
7693
                drawButton(mActInstance, true);
7694
            }
7695
        }
7696
#else
206 andreas 7697
        else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT)    // Scale to fit (disabled)
195 andreas 7698
        {
7699
            if (sr[0].oo < 0)
7700
            {
7701
                sr[0].oo = 128;
7702
                mChanged = true;
7703
                mActInstance = 0;
7704
                drawButton(mActInstance, true);
7705
            }
7706
        }
206 andreas 7707
        else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER)    // Show banner
195 andreas 7708
        {
7709
            if (pressed)
7710
            {
7711
                TConfig::setTemporary(true);
7712
                bool st = TConfig::showBanner();
7713
                mActInstance = instance = (st ? 0 : 1);
7714
                mChanged = true;
7715
                TConfig::saveBanner(st);
7716
                drawButton(mActInstance, true);
7717
            }
7718
        }
7719
#endif
206 andreas 7720
        else if (isSystem && ch == SYSTEM_ITEM_VIEWNOTOOLBAR)    // Suppress toolbar
195 andreas 7721
        {
7722
            if (pressed)
7723
            {
7724
                TConfig::setTemporary(true);
7725
                bool st = TConfig::getToolbarSuppress();
7726
                mActInstance = instance = (st ? 0 : 1);
7727
                mChanged = true;
7728
                TConfig::saveToolbarSuppress(!st);
7729
                drawButton(mActInstance, true);
7730
            }
7731
        }
206 andreas 7732
        else if (isSystem && ch == SYSTEM_ITEM_VIEWTOOLBAR)    // Force toolbar
195 andreas 7733
        {
7734
            if (pressed)
7735
            {
7736
                TConfig::setTemporary(true);
198 andreas 7737
 
7738
                if (TConfig::getToolbarSuppress())
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
                }
7748
                else
7749
                {
7750
                    if (sr[0].oo >= 0)
7751
                        sr[0].oo = -1;
7752
 
7753
                    bool st = TConfig::getToolbarForce();
7754
                    mActInstance = instance = (st ? 0 : 1);
7755
                    mChanged = true;
7756
                    TConfig::saveToolbarForce(!st);
7757
                    drawButton(mActInstance, true);
7758
                }
195 andreas 7759
            }
7760
        }
206 andreas 7761
        else if (isSystem && ch == SYSTEM_ITEM_VIEWROTATE)    // Lock rotation
195 andreas 7762
        {
7763
            if (pressed)
7764
            {
7765
                TConfig::setTemporary(true);
7766
                bool st = TConfig::getRotationFixed();
7767
                mActInstance = instance = (st ? 0 : 1);
7768
                mChanged = true;
7769
                TConfig::setRotationFixed(!st);
7770
                drawButton(mActInstance, true);
7771
            }
7772
        }
71 andreas 7773
        else if (fb == FB_MOMENTARY)
7774
        {
7775
            if (pressed)
15 andreas 7776
                instance = 1;
14 andreas 7777
            else
15 andreas 7778
                instance = 0;
7779
 
71 andreas 7780
            MSG_DEBUG("Flavor FB_MOMENTARY, instance=" << instance);
15 andreas 7781
            mActInstance = instance;
176 andreas 7782
            mChanged = true;
163 andreas 7783
 
7784
            if (pushFunc.empty() || (!pushFunc.empty() && instance == 0))
7785
                drawButton(instance);
7786
 
15 andreas 7787
            // If there is nothing in "hs", then it depends on the pixel of the
7788
            // layer. Only if the pixel the coordinates point to are not
7789
            // transparent, the button takes the click.
31 andreas 7790
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 7791
                return false;
7792
 
79 andreas 7793
            // Play sound, if one is defined
90 andreas 7794
            if (gPageManager)
7795
            {
165 andreas 7796
                if (pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
141 andreas 7797
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
165 andreas 7798
                else if (!pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
141 andreas 7799
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
90 andreas 7800
            }
79 andreas 7801
 
15 andreas 7802
            if (pushFunc.empty())   // Don't draw the button if it has a push function defined
7803
                showLastButton();
7804
            else
14 andreas 7805
                mActInstance = 0;
15 andreas 7806
        }
7807
        else if (fb == FB_CHANNEL || fb == FB_NONE)
7808
        {
7809
            if (pressed)
7810
                instance = 1;
7811
            else
7812
                instance = 0;
14 andreas 7813
 
71 andreas 7814
            MSG_DEBUG("Flavor FB_CHANNEL, instance=" << instance);
15 andreas 7815
            // If there is nothing in "hs", then it depends on the pixel of the
7816
            // layer. Only if the pixel the coordinates point to are not
7817
            // transparent, the button takes the click.
31 andreas 7818
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 7819
                return false;
14 andreas 7820
        }
15 andreas 7821
        else if (fb == FB_INV_CHANNEL)
7822
        {
7823
            if (pressed)
7824
                instance = 0;
7825
            else
7826
                instance = 1;
14 andreas 7827
 
71 andreas 7828
            MSG_DEBUG("Flavor FB_INV_CHANNEL, instance=" << instance);
15 andreas 7829
            // If there is nothing in "hs", then it depends on the pixel of the
7830
            // layer. Only if the pixel the coordinates point to are not
7831
            // transparent, the button takes the click.
31 andreas 7832
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 7833
                return false;
79 andreas 7834
 
7835
            // Play sound, if one is defined
90 andreas 7836
            if (gPageManager)
7837
            {
165 andreas 7838
                if (pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
141 andreas 7839
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
165 andreas 7840
                else if (!pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
141 andreas 7841
                    gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
90 andreas 7842
            }
15 andreas 7843
        }
7844
        else if (fb == FB_ALWAYS_ON)
14 andreas 7845
        {
154 andreas 7846
            int oldInst = mActInstance;
15 andreas 7847
            instance = 1;
7848
            mActInstance = 1;
71 andreas 7849
            MSG_DEBUG("Flavor FB_ALWAYS_ON, instance=" << instance);
154 andreas 7850
 
7851
            if (oldInst != mActInstance)        // This should never become true!
176 andreas 7852
            {
7853
                mChanged = true;
154 andreas 7854
                drawButton(instance, false);
176 andreas 7855
            }
154 andreas 7856
 
15 andreas 7857
            // If there is nothing in "hs", then it depends on the pixel of the
7858
            // layer. Only if the pixel the coordinates point to are not
7859
            // transparent, the button takes the click.
31 andreas 7860
            if (hs.empty() && isPixelTransparent(sx, sy))
15 andreas 7861
                return false;
7862
 
79 andreas 7863
            // Play sound, if one is defined
165 andreas 7864
            if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
141 andreas 7865
                gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
71 andreas 7866
        }
7867
 
15 andreas 7868
        if ((cp && ch) || !op.empty())
7869
        {
14 andreas 7870
            scmd.device = TConfig::getChannel();
7871
            scmd.port = cp;
7872
            scmd.channel = ch;
7873
 
15 andreas 7874
            if (op.empty())
7875
            {
7876
                if (instance)
7877
                    scmd.MC = 0x0084;
7878
                else
7879
                    scmd.MC = 0x0085;
7880
            }
14 andreas 7881
            else
15 andreas 7882
            {
7883
                scmd.MC = 0x008b;
7884
                scmd.msg = op;
7885
            }
14 andreas 7886
 
7887
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
7888
            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") << ")");
7889
 
7890
            if (gAmxNet)
15 andreas 7891
            {
7892
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
7893
                    gAmxNet->sendCommand(scmd);
7894
            }
14 andreas 7895
            else
7896
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
7897
        }
7898
    }
15 andreas 7899
    else if (type == MULTISTATE_GENERAL)
7900
    {
79 andreas 7901
        // Play sound, if one is defined
165 andreas 7902
        if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[mActInstance].sd.empty() && strCaseCompare(sr[mActInstance].sd, "None") != 0)
141 andreas 7903
            gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[mActInstance].sd);
79 andreas 7904
 
15 andreas 7905
        if ((cp && ch) || !op.empty())
7906
        {
7907
            scmd.device = TConfig::getChannel();
7908
            scmd.port = cp;
7909
            scmd.channel = ch;
14 andreas 7910
 
15 andreas 7911
            if (op.empty())
7912
            {
7913
                if (pressed || fb == FB_ALWAYS_ON)
7914
                    scmd.MC = 0x0084;
7915
                else
7916
                    scmd.MC = 0x0085;
7917
            }
7918
            else
7919
            {
7920
                scmd.MC = 0x008b;
7921
                scmd.msg = op;
7922
            }
7923
 
7924
            MSG_DEBUG("Button " << bi << ", " << na << " with handle " << TObject::handleToString(mHandle));
7925
            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") << ")");
7926
 
7927
            if (gAmxNet)
7928
            {
7929
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
7930
                    gAmxNet->sendCommand(scmd);
7931
            }
7932
            else
7933
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
7934
        }
7935
    }
49 andreas 7936
    else if (type == BARGRAPH && lf.find("active") != string::npos)
7937
    {
7938
        // Find the click position
7939
        int level = 0;
15 andreas 7940
 
49 andreas 7941
        if (dr.compare("horizontal") == 0)
7942
        {
7943
            level = (ri ? (wt - x) : x);
7944
            level = (int)((double)(rh - rl) / (double)wt * (double)level);
7945
        }
7946
        else
7947
        {
7948
            level = (ri ? y : (ht - y));
7949
            level = (int)((double)(rh - rl) / (double)ht * (double)level);
7950
        }
7951
 
7952
        // Draw the bargraph
7953
        if (!drawBargraph(mActInstance, level, visible))
7954
            return false;
7955
 
7956
        // Handle click
192 andreas 7957
        if (isSystem && lv == 9)    // System volume button
49 andreas 7958
        {
141 andreas 7959
            TConfig::saveSystemVolume(level);
7960
            TConfig::saveSettings();
7961
        }
7962
        else if ((cp && ch) || !op.empty())
7963
        {
49 andreas 7964
            scmd.device = TConfig::getChannel();
7965
            scmd.port = cp;
7966
            scmd.channel = ch;
7967
 
7968
            if (op.empty())
7969
            {
7970
                if (pressed || fb == FB_ALWAYS_ON)
7971
                    scmd.MC = 0x0084;
7972
                else
7973
                    scmd.MC = 0x0085;
7974
            }
7975
            else
7976
            {
7977
                scmd.MC = 0x008b;
7978
                scmd.msg = op;
7979
            }
7980
 
7981
            if (gAmxNet)
7982
            {
7983
                if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
7984
                    gAmxNet->sendCommand(scmd);
7985
            }
7986
            else
7987
                MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
7988
        }
7989
 
7990
        // Send the level
7991
        if (lp && lv)
7992
        {
7993
            scmd.device = TConfig::getChannel();
7994
            scmd.port = lp;
7995
            scmd.channel = lv;
7996
            scmd.level = lv;
7997
            scmd.value = level;
7998
            scmd.MC = 0x008a;
7999
 
8000
            if (gAmxNet)
8001
                gAmxNet->sendCommand(scmd);
8002
        }
8003
    }
50 andreas 8004
    else if (type == TEXT_INPUT)
8005
    {
8006
        MSG_DEBUG("Text area detected. Switching on keyboard");
8007
        // Drawing background graphic (visible part of area)
8008
        drawTextArea(mActInstance);
8009
    }
49 andreas 8010
 
15 andreas 8011
    /* FIXME: Move the following to class TPageManager!
8012
     *        To do that, the preconditions are to be implemented. It must be
8013
     *        possible to find the button and get access to the credentials
8014
     *        of it.
8015
     */
14 andreas 8016
    if (!pushFunc.empty() && pressed)
8017
    {
53 andreas 8018
        MSG_DEBUG("Executing a push function ...");
14 andreas 8019
        vector<PUSH_FUNC_T>::iterator iter;
8020
 
118 andreas 8021
        for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
14 andreas 8022
        {
168 andreas 8023
            MSG_DEBUG("Testing for function \"" << iter->pfType << "\"");
53 andreas 8024
 
163 andreas 8025
            if (fb == FB_MOMENTARY || fb == FB_NONE)
8026
                mActInstance = 0;
8027
            else if (fb == FB_ALWAYS_ON || fb == FB_INV_CHANNEL)
8028
                mActInstance = 1;
8029
 
168 andreas 8030
            if (strCaseCompare(iter->pfType, "SSHOW") == 0)            // show popup
11 andreas 8031
            {
14 andreas 8032
                if (gPageManager)
8033
                    gPageManager->showSubPage(iter->pfName);
11 andreas 8034
            }
168 andreas 8035
            else if (strCaseCompare(iter->pfType, "SHIDE") == 0)       // hide popup
11 andreas 8036
            {
14 andreas 8037
                if (gPageManager)
8038
                    gPageManager->hideSubPage(iter->pfName);
11 andreas 8039
            }
168 andreas 8040
            else if (strCaseCompare(iter->pfType, "SCGROUP") == 0)     // hide group
14 andreas 8041
            {
8042
                if (gPageManager)
8043
                    gPageManager->closeGroup(iter->pfName);
8044
            }
168 andreas 8045
            else if (strCaseCompare(iter->pfType, "STAN") == 0)        // Flip to standard page
14 andreas 8046
            {
8047
                if (gPageManager)
8048
                {
168 andreas 8049
                    if (!iter->pfName.empty())
8050
                        gPageManager->setPage(iter->pfName);
8051
                    else
15 andreas 8052
                    {
168 andreas 8053
                        TPage *page = gPageManager->getActualPage();
15 andreas 8054
 
168 andreas 8055
                        if (!page)
8056
                        {
8057
                            MSG_DEBUG("Internal error: No actual page found!");
8058
                            return false;
8059
                        }
15 andreas 8060
 
168 andreas 8061
                        TSettings *settings = gPageManager->getSettings();
8062
 
205 andreas 8063
                        if (settings && settings->getPowerUpPage().compare(page->getName()) != 0)
168 andreas 8064
                            gPageManager->setPage(settings->getPowerUpPage());
8065
                    }
15 andreas 8066
                }
8067
            }
168 andreas 8068
            else if (strCaseCompare(iter->pfType, "FORGET") == 0)      // Flip to page and forget
15 andreas 8069
            {
168 andreas 8070
                if (gPageManager && !iter->pfName.empty())
8071
                        gPageManager->setPage(iter->pfName, true);
8072
            }
8073
            else if (strCaseCompare(iter->pfType, "PREV") == 0)        // Flip to previous page
8074
            {
15 andreas 8075
                if (gPageManager)
8076
                {
8077
                    int old = gPageManager->getPreviousPageNumber();
8078
 
8079
                    if (old > 0)
8080
                        gPageManager->setPage(old);
8081
                }
8082
            }
168 andreas 8083
            else if (strCaseCompare(iter->pfType, "STOGGLE") == 0)     // Toggle popup state
15 andreas 8084
            {
146 andreas 8085
                if (!iter->pfName.empty() && gPageManager)
15 andreas 8086
                {
14 andreas 8087
                    TSubPage *page = gPageManager->getSubPage(iter->pfName);
11 andreas 8088
 
146 andreas 8089
                    if (!page)      // Is the page not in cache?
8090
                    {               // No, then load it
8091
                        gPageManager->showSubPage(iter->pfName);
8092
                        return true;
8093
                    }
8094
 
8095
                    if (page->isVisible())
14 andreas 8096
                        gPageManager->hideSubPage(iter->pfName);
146 andreas 8097
                    else
14 andreas 8098
                        gPageManager->showSubPage(iter->pfName);
8099
                }
8100
            }
168 andreas 8101
            else if (strCaseCompare(iter->pfType, "SCPANEL") == 0)   // Hide all popups
146 andreas 8102
            {
8103
                if (gPageManager)
8104
                {
8105
                    TSubPage *page = gPageManager->getFirstSubPage();
8106
 
8107
                    while (page)
8108
                    {
8109
                        page->drop();
8110
                        page = gPageManager->getNextSubPage();
8111
                    }
8112
                }
8113
            }
8114
            else
8115
            {
8116
                MSG_WARNING("Unknown page flip command " << iter->pfType);
8117
            }
11 andreas 8118
        }
8119
    }
15 andreas 8120
 
147 andreas 8121
    if (!cm.empty() && co == 0 && pressed)      // Feed command to ourself?
8122
    {                                           // Yes, then feed it into command queue.
149 andreas 8123
        MSG_DEBUG("Button has a self feed command");
147 andreas 8124
 
151 andreas 8125
        int channel = TConfig::getChannel();
8126
        int system = TConfig::getSystem();
8127
 
147 andreas 8128
        if (gPageManager)
8129
        {
8130
            amx::ANET_COMMAND cmd;
8131
            cmd.MC = 0x000c;
151 andreas 8132
            cmd.device1 = channel;
8133
            cmd.port1 = ap;
8134
            cmd.system = system;
8135
            cmd.data.message_string.device = channel;
8136
            cmd.data.message_string.port = ap;  // Must be the address port of button
8137
            cmd.data.message_string.system = system;
8138
            cmd.data.message_string.type = 1;   // 8 bit char string
149 andreas 8139
 
8140
            vector<string>::iterator iter;
8141
 
8142
            for (iter = cm.begin(); iter != cm.end(); ++iter)
8143
            {
8144
                cmd.data.message_string.length = iter->length();
8145
                memset(&cmd.data.message_string.content, 0, sizeof(cmd.data.message_string.content));
205 andreas 8146
                strncpy((char *)&cmd.data.message_string.content, iter->c_str(), sizeof(cmd.data.message_string.content)-1);
149 andreas 8147
                gPageManager->doCommand(cmd);
8148
            }
147 andreas 8149
        }
8150
    }
8151
    else if (!cm.empty() && pressed)
8152
    {
149 andreas 8153
        MSG_DEBUG("Button sends a command on port " << co);
147 andreas 8154
 
8155
        if (gPageManager)
149 andreas 8156
        {
8157
            vector<string>::iterator iter;
8158
 
8159
            for (iter = cm.begin(); iter != cm.end(); ++iter)
8160
                gPageManager->sendCommandString(co, *iter);
8161
        }
147 andreas 8162
    }
8163
 
15 andreas 8164
    return true;
11 andreas 8165
}
8166
 
7 andreas 8167
/**
8168
 * Based on the pixels in the \a basePix, the function decides whether to return
8169
 * the value of \a col1 or \a col2. A red pixel returns the color \a col1 and
8170
 * a green pixel returns the color \a col2. If there is no red and no green
8171
 * pixel, a transparent pixel is returned.
8172
 *
8173
 * @param basePix
8174
 * This is a pixel from a mask containing red and/or green pixels.
8175
 *
8176
 * @param maskPix
8177
 * This is a pixel from a mask containing more or less tranparent pixels. If
8178
 * the alpha channel of \a basePix is 0 (transparent) this pixel is returned.
8179
 *
8180
 * @param col1
8181
 * The first color.
8182
 *
8183
 * @param col2
8184
 * The second color.
8185
 *
8186
 * @return
8187
 * An array containing the color for one pixel.
8188
 */
10 andreas 8189
SkColor TButton::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
7 andreas 8190
{
8191
    uint alpha = SkColorGetA(basePix);
23 andreas 8192
    uint green = SkColorGetG(basePix);
241 andreas 8193
#if defined(__ANDROID__) || defined(__APPLE__)
240 andreas 8194
    uint red = SkColorGetB(basePix);
8195
#else
7 andreas 8196
    uint red = SkColorGetR(basePix);
23 andreas 8197
#endif
7 andreas 8198
 
241 andreas 8199
    if (alpha == 0)
7 andreas 8200
        return maskPix;
8201
 
8202
    if (red && green)
8203
    {
241 andreas 8204
        if (red < green)
8205
            return col2;
8206
 
171 andreas 8207
        return col1;
7 andreas 8208
    }
8209
 
8210
    if (red)
8211
        return col1;
8212
 
8213
    if (green)
8214
        return col2;
8215
 
8216
    return SK_ColorTRANSPARENT; // transparent pixel
8217
}
8218
 
8219
TEXT_EFFECT TButton::textEffect(const std::string& effect)
8220
{
8221
    DECL_TRACER("TButton::textEffect(const std::string& effect)");
8222
 
8223
    if (effect == "Outline-S")
8224
        return EFFECT_OUTLINE_S;
8225
    else if (effect == "Outline-M")
8226
        return EFFECT_OUTLINE_M;
8227
    else if (effect == "Outline-L")
8228
        return EFFECT_OUTLINE_L;
8229
    else if (effect == "Outline-X")
8230
        return EFFECT_OUTLINE_X;
8231
    else if (effect == "Glow-S")
8232
        return EFFECT_GLOW_S;
8233
    else if (effect == "Glow-M")
8234
        return EFFECT_GLOW_M;
8235
    else if (effect == "Glow-L")
8236
        return EFFECT_GLOW_L;
8237
    else if (effect == "Glow-X")
8238
        return EFFECT_GLOW_X;
8239
    else if (effect == "Soft Drop Shadow 1")
8240
        return EFFECT_SOFT_DROP_SHADOW_1;
8241
    else if (effect == "Soft Drop Shadow 2")
8242
        return EFFECT_SOFT_DROP_SHADOW_2;
8243
    else if (effect == "Soft Drop Shadow 3")
8244
        return EFFECT_SOFT_DROP_SHADOW_3;
8245
    else if (effect == "Soft Drop Shadow 4")
8246
        return EFFECT_SOFT_DROP_SHADOW_4;
8247
    else if (effect == "Soft Drop Shadow 5")
8248
        return EFFECT_SOFT_DROP_SHADOW_5;
8249
    else if (effect == "Soft Drop Shadow 6")
8250
        return EFFECT_SOFT_DROP_SHADOW_6;
8251
    else if (effect == "Soft Drop Shadow 7")
8252
        return EFFECT_SOFT_DROP_SHADOW_7;
8253
    else if (effect == "Soft Drop Shadow 8")
8254
        return EFFECT_SOFT_DROP_SHADOW_8;
8255
    else if (effect == "Medium Drop Shadow 1")
8256
        return EFFECT_MEDIUM_DROP_SHADOW_1;
8257
    else if (effect == "Medium Drop Shadow 2")
8258
        return EFFECT_MEDIUM_DROP_SHADOW_2;
8259
    else if (effect == "Medium Drop Shadow 3")
8260
        return EFFECT_MEDIUM_DROP_SHADOW_3;
8261
    else if (effect == "Medium Drop Shadow 4")
8262
        return EFFECT_MEDIUM_DROP_SHADOW_4;
8263
    else if (effect == "Medium Drop Shadow 5")
8264
        return EFFECT_MEDIUM_DROP_SHADOW_5;
8265
    else if (effect == "Medium Drop Shadow 6")
8266
        return EFFECT_MEDIUM_DROP_SHADOW_6;
8267
    else if (effect == "Medium Drop Shadow 7")
8268
        return EFFECT_MEDIUM_DROP_SHADOW_7;
8269
    else if (effect == "Medium Drop Shadow 8")
8270
        return EFFECT_MEDIUM_DROP_SHADOW_8;
8271
    else if (effect == "Hard Drop Shadow 1")
8272
        return EFFECT_HARD_DROP_SHADOW_1;
8273
    else if (effect == "Hard Drop Shadow 2")
8274
        return EFFECT_HARD_DROP_SHADOW_2;
8275
    else if (effect == "Hard Drop Shadow 3")
8276
        return EFFECT_HARD_DROP_SHADOW_3;
8277
    else if (effect == "Hard Drop Shadow 4")
8278
        return EFFECT_HARD_DROP_SHADOW_4;
8279
    else if (effect == "Hard Drop Shadow 5")
8280
        return EFFECT_HARD_DROP_SHADOW_5;
8281
    else if (effect == "Hard Drop Shadow 6")
8282
        return EFFECT_HARD_DROP_SHADOW_6;
8283
    else if (effect == "Hard Drop Shadow 7")
8284
        return EFFECT_HARD_DROP_SHADOW_7;
8285
    else if (effect == "Hard Drop Shadow 8")
8286
        return EFFECT_HARD_DROP_SHADOW_8;
8287
    else if (effect == "Soft Drop Shadow 1 with outline")
8288
        return EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE;
8289
    else if (effect == "Soft Drop Shadow 2 with outline")
8290
        return EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE;
8291
    else if (effect == "Soft Drop Shadow 3 with outline")
8292
        return EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE;
8293
    else if (effect == "Soft Drop Shadow 4 with outline")
8294
        return EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE;
8295
    else if (effect == "Soft Drop Shadow 5 with outline")
8296
        return EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE;
8297
    else if (effect == "Soft Drop Shadow 6 with outline")
8298
        return EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE;
8299
    else if (effect == "Soft Drop Shadow 7 with outline")
8300
        return EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE;
8301
    else if (effect == "Soft Drop Shadow 8 with outline")
8302
        return EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE;
8303
    else if (effect == "Medium Drop Shadow 1 with outline")
8304
        return EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE;
8305
    else if (effect == "Medium Drop Shadow 2 with outline")
8306
        return EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE;
8307
    else if (effect == "Medium Drop Shadow 3 with outline")
8308
        return EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE;
8309
    else if (effect == "Medium Drop Shadow 4 with outline")
8310
        return EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE;
8311
    else if (effect == "Medium Drop Shadow 5 with outline")
8312
        return EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE;
8313
    else if (effect == "Medium Drop Shadow 6 with outline")
8314
        return EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE;
8315
    else if (effect == "Medium Drop Shadow 7 with outline")
8316
        return EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE;
8317
    else if (effect == "Medium Drop Shadow 8 with outline")
8318
        return EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE;
8319
    else if (effect == "Hard Drop Shadow 1 with outline")
8320
        return EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE;
8321
    else if (effect == "Hard Drop Shadow 2 with outline")
8322
        return EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE;
8323
    else if (effect == "Hard Drop Shadow 3 with outline")
8324
        return EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE;
8325
    else if (effect == "Hard Drop Shadow 4 with outline")
8326
        return EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE;
8327
    else if (effect == "Hard Drop Shadow 5 with outline")
8328
        return EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE;
8329
    else if (effect == "Hard Drop Shadow 6 with outline")
8330
        return EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE;
8331
    else if (effect == "Hard Drop Shadow 7 with outline")
8332
        return EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE;
8333
    else if (effect == "Hard Drop Shadow 8 with outline")
8334
        return EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE;
8335
 
8336
    return EFFECT_NONE;
8337
}
15 andreas 8338
 
8339
bool TButton::isSystemButton()
8340
{
8341
    DECL_TRACER("TButton::isSystemButton()");
8342
 
195 andreas 8343
    if (type == MULTISTATE_BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
8344
        return true;
8345
    else if (type == BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
8346
        return true;
206 andreas 8347
    else if (type == LISTBOX && ap == 0 && ad > 0 && ti >= SYSTEM_PAGE_START)
198 andreas 8348
        return true;
195 andreas 8349
    else if (ap == 0 && TSystem::isSystemButton(ad))
8350
        return true;
8351
    else if (cp == 0 && TSystem::isSystemButton(ch))
8352
        return true;
15 andreas 8353
 
8354
    return false;
8355
}
21 andreas 8356
 
8357
THR_REFRESH_t *TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)
8358
{
8359
    DECL_TRACER("TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)");
8360
 
8361
    THR_REFRESH_t *p = mThrRefresh;
8362
    THR_REFRESH_t *r, *last = p;
8363
 
8364
    if (!refr || !handle || !parent || bi <= 0)
8365
    {
8366
        MSG_ERROR("Invalid parameter!");
8367
        return nullptr;
8368
    }
8369
 
8370
    r = new THR_REFRESH_t;
8371
    r->mImageRefresh = refr;
8372
    r->handle = handle;
8373
    r->parent = parent;
8374
    r->bi = bi;
8375
    r->next = nullptr;
8376
 
8377
    // If the chain is empty, add the new item;
8378
    if (!mThrRefresh)
8379
        mThrRefresh = r;
8380
    else    // Find the end and append the item
8381
    {
8382
        while (p)
8383
        {
8384
            last = p;
8385
 
8386
            if (p->handle == handle && p->parent == parent && p->bi == bi)
8387
            {
8388
                MSG_WARNING("Duplicate button found! Didn't add it again.");
8389
                delete r;
8390
                return p;
8391
            }
8392
 
8393
            p = p->next;
8394
        }
8395
 
8396
        last->next = r;
8397
    }
8398
 
8399
    MSG_DEBUG("New dynamic button added.");
8400
    return r;
8401
}
8402
 
8403
THR_REFRESH_t *TButton::_findResource(ulong handle, ulong parent, int bi)
8404
{
8405
    DECL_TRACER("TButton::_findResource(ulong handle, ulong parent, int bi)");
8406
 
8407
    THR_REFRESH_t *p = mThrRefresh;
8408
 
8409
    while (p)
8410
    {
8411
        if (p->handle == handle && p->parent == parent && p->bi == bi)
8412
            return p;
8413
 
8414
        p = p->next;
8415
    }
8416
 
8417
    return nullptr;
8418
}
94 andreas 8419
 
8420
void TButton::addToBitmapCache(BITMAP_CACHE& bc)
8421
{
8422
    DECL_TRACER("TButton::addToBitmapCache(BITMAP_CACHE& bc)");
8423
 
8424
    mutex_bmCache.lock();
8425
 
8426
    if (nBitmapCache.size() == 0)
8427
    {
8428
        nBitmapCache.push_back(bc);
8429
        mutex_bmCache.unlock();
8430
        return;
8431
    }
8432
 
8433
    vector<BITMAP_CACHE>::iterator iter;
8434
 
8435
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8436
    {
8437
        if (iter->handle == bc.handle && iter->parent == bc.parent && iter->bi == bc.bi)
8438
        {
97 andreas 8439
            nBitmapCache.erase(iter);
8440
            nBitmapCache.push_back(bc);
94 andreas 8441
            mutex_bmCache.unlock();
8442
            return;
8443
        }
8444
    }
8445
 
8446
    nBitmapCache.push_back(bc);
8447
    mutex_bmCache.unlock();
8448
}
8449
 
8450
BITMAP_CACHE& TButton::getBCentryByHandle(ulong handle, ulong parent)
8451
{
8452
    DECL_TRACER("TButton::getBCentryByHandle(ulong handle, ulong parent)");
8453
 
8454
    mutex_bmCache.lock();
8455
 
8456
    if (nBitmapCache.size() == 0)
8457
    {
8458
        mutex_bmCache.unlock();
8459
        return mBCDummy;
8460
    }
8461
 
8462
    vector<BITMAP_CACHE>::iterator iter;
8463
 
8464
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8465
    {
8466
        if (iter->handle == handle && iter->parent == parent)
8467
        {
8468
            mutex_bmCache.unlock();
8469
            return *iter;
8470
        }
8471
    }
8472
 
8473
    mutex_bmCache.unlock();
8474
    return mBCDummy;
8475
}
8476
 
8477
BITMAP_CACHE& TButton::getBCentryByBI(int bIdx)
8478
{
8479
    DECL_TRACER("TButton::getBCentryByBI(int bIdx)");
8480
 
8481
    mutex_bmCache.lock();
8482
 
8483
    if (nBitmapCache.size() == 0)
8484
    {
8485
        mutex_bmCache.unlock();
8486
        return mBCDummy;
8487
    }
8488
 
8489
    vector<BITMAP_CACHE>::iterator iter;
8490
 
8491
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8492
    {
8493
        if (iter->bi == bIdx)
8494
        {
8495
            mutex_bmCache.unlock();
8496
            return *iter;
8497
        }
8498
    }
8499
 
8500
    mutex_bmCache.unlock();
8501
    return mBCDummy;
8502
}
8503
 
8504
void TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)
8505
{
8506
    DECL_TRACER("TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)");
8507
 
8508
    if (nBitmapCache.size() == 0 || !elem)
8509
        return;
8510
 
8511
    mutex_bmCache.lock();
8512
    vector<BITMAP_CACHE>::iterator iter;
8513
 
8514
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8515
    {
8516
        if (iter == *elem)
8517
        {
8518
            nBitmapCache.erase(iter);
8519
            mutex_bmCache.unlock();
8520
            return;
8521
        }
8522
    }
8523
 
8524
    mutex_bmCache.unlock();
8525
}
8526
 
8527
void TButton::setReady(ulong handle)
8528
{
8529
    DECL_TRACER("TButton::setReady(ulong handle)");
8530
 
8531
    if (nBitmapCache.size() == 0)
8532
        return;
8533
 
8534
    mutex_bmCache.lock();
8535
    vector<BITMAP_CACHE>::iterator iter;
8536
 
8537
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8538
    {
8539
        if (iter->handle == handle)
8540
        {
8541
            iter->ready = true;
8542
            mutex_bmCache.unlock();
8543
            return;
8544
        }
8545
    }
8546
 
8547
    mutex_bmCache.unlock();
8548
}
8549
 
97 andreas 8550
void TButton::setInvalid(ulong handle)
8551
{
8552
    DECL_TRACER("TButton::setInvalid(ulong handle)");
8553
 
8554
    if (nBitmapCache.size() == 0)
8555
        return;
8556
 
8557
    mutex_bmCache.lock();
8558
    vector<BITMAP_CACHE>::iterator iter;
8559
 
8560
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8561
    {
8562
        if (iter->handle == handle)
8563
        {
8564
            nBitmapCache.erase(iter);
8565
            mutex_bmCache.unlock();
8566
            return;
8567
        }
8568
    }
8569
 
8570
    mutex_bmCache.unlock();
8571
}
8572
 
94 andreas 8573
void TButton::setBCBitmap(ulong handle, SkBitmap& bm)
8574
{
8575
    DECL_TRACER("TButton::setBCBitmap(ulong handle, SkBitmap& bm)");
8576
 
8577
    if (nBitmapCache.size() == 0)
8578
        return;
8579
 
8580
    mutex_bmCache.lock();
8581
    vector<BITMAP_CACHE>::iterator iter;
8582
 
8583
    for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8584
    {
8585
        if (iter->handle == handle)
8586
        {
8587
            iter->bitmap = bm;
8588
            mutex_bmCache.unlock();
8589
            return;
8590
        }
8591
    }
8592
 
8593
    mutex_bmCache.unlock();
8594
}
8595
 
8596
void TButton::showBitmapCache()
8597
{
8598
    DECL_TRACER("TButton::showBitmapCache()");
8599
 
8600
    mutex_bmCache.lock();
8601
    vector<BITMAP_CACHE>::iterator iter;
8602
    bool found;
8603
 
8604
    while (nBitmapCache.size() > 0)
8605
    {
8606
        found = false;
8607
 
8608
        for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
8609
        {
8610
            if (iter->ready)
8611
            {
8612
                if (_displayButton)
176 andreas 8613
                {
94 andreas 8614
                    _displayButton(iter->handle, iter->parent, (unsigned char *)iter->bitmap.getPixels(), iter->width, iter->height, iter->bitmap.info().minRowBytes(), iter->left, iter->top);
176 andreas 8615
                    mChanged = false;
8616
                }
94 andreas 8617
 
8618
                nBitmapCache.erase(iter);
8619
                found = true;
8620
                break;
8621
            }
8622
        }
8623
 
8624
        if (!found)
8625
            break;
8626
    }
8627
 
8628
    mutex_bmCache.unlock();
8629
}
159 andreas 8630
 
8631
uint32_t TButton::pixelMix(uint32_t s, uint32_t d, uint32_t a, PMIX mix)
8632
{
8633
    DECL_TRACER("TButton::pixelMultiply(uint32_t s, uint32_t d)");
8634
 
8635
    uint32_t r = 0;
8636
 
8637
    switch(mix)
8638
    {
8639
        case PMIX_SRC:          r = s; break;                                                   // SRC
8640
        case PMIX_DST:          r = d; break;                                                   // DST
8641
        case PMIX_MULTIPLY:     r = s * (255 - (d * a)) + d * (255 - (s * a)) + s * d; break;   // Multiply
8642
        case PMIX_PLUS:         r = std::min(s + d, (uint32_t)255); break;                      // plus
8643
        case PMIX_XOR:          r = s * (255 - (d * a)) + d * (255 - (s * a)); break;           // XOr
8644
        case PMIX_DSTTOP:       r = d * (s * a) + s * (255 - (d * a)); break;                   // DstATop
8645
        case PMIX_SRCTOP:       r = s * (d * a) + d * (255 - (s * a)); break;                   // SrcATop
8646
        case PMIX_SRCOVER:      r = s + (255 - (s * a)) * d; break;                             // SrcOver
8647
        case PMIX_SCREEN:       r = s + d - s * d; break;                                       // Screen
8648
    }
8649
 
8650
    return r & 0x00ff;
8651
}
225 andreas 8652
 
8653
bool TButton::setListSource(const string &source, const vector<string>& configs)
8654
{
8655
    DECL_TRACER("TButton::setListSource(const string &source, const vector<string>& configs)");
8656
 
8657
    TUrl url;
8658
 
227 andreas 8659
    listSourceUser.clear();
8660
    listSourcePass.clear();
8661
    listSourceCsv = false;
8662
    listSourceHasHeader = false;
8663
 
8664
    if (configs.size() > 0)
8665
    {
8666
        vector<string>::const_iterator iter;
8667
 
8668
        for (iter = configs.begin(); iter != configs.end(); ++iter)
8669
        {
8670
            size_t pos;
8671
 
8672
            if ((pos = iter->find("user=")) != string::npos)
8673
                listSourceUser = iter->substr(pos + 5);
8674
            else if ((pos = iter->find("pass=")) != string::npos)
8675
                listSourcePass = iter->substr(pos + 5);
8676
            else if (iter->find("csv=") != string::npos)
8677
            {
8678
                string str = *iter;
8679
                string low = toLower(str);
8680
 
8681
                if (low.find("true") != string::npos || low.find("1") != string::npos)
8682
                    listSourceCsv = true;
8683
            }
8684
            else if (iter->find("has_header=") != string::npos)
8685
            {
8686
                string str = *iter;
8687
                string low = toLower(str);
8688
 
8689
                if (low.find("true") != string::npos || low.find("1") != string::npos)
8690
                    listSourceHasHeader = true;
8691
            }
8692
        }
8693
    }
8694
 
225 andreas 8695
    if (!url.setUrl(source))    // Dynamic source?
8696
    {
8697
        size_t idx = 0;
8698
 
8699
        if (!gPrjResources)
8700
            return false;
8701
 
8702
        if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
8703
        {
8704
            MSG_ERROR("There exists no image resource!");
8705
            return false;
8706
        }
8707
 
8708
        RESOURCE_T resource = gPrjResources->findResource(idx, source);
8709
 
8710
        if (resource.protocol.empty())
8711
        {
8712
            MSG_WARNING("Resource " << source << " not found!");
8713
            return false;
8714
        }
8715
 
8716
        listSource = resource.protocol + "://";
8717
 
227 andreas 8718
        if (!resource.user.empty() || !listSourceUser.empty())
225 andreas 8719
        {
227 andreas 8720
            listSource += ((listSourceUser.empty() == false) ? listSourceUser : resource.user);
225 andreas 8721
 
227 andreas 8722
            if ((!resource.password.empty() && !resource.encrypted) || !listSourcePass.empty())
8723
                listSource += ":" + ((listSourcePass.empty() == false) ? listSourcePass : resource.password);
225 andreas 8724
 
8725
            listSource += "@";
8726
        }
8727
 
8728
        listSource += resource.host;
8729
 
8730
        if (!resource.path.empty())
8731
            listSource += "/" + resource.path;
8732
 
8733
        if (!resource.file.empty())
8734
            listSource += "/" + resource.file;
8735
 
8736
        return true;
8737
    }
8738
 
8739
    listSource = source;
8740
    return true;
8741
}
227 andreas 8742
 
8743
bool TButton::setListSourceFilter(const string& filter)
8744
{
8745
    DECL_TRACER("TButton::setListSourceFilter(const string& filter)");
8746
 
8747
    if (filter.empty())
8748
        return false;
8749
 
230 andreas 8750
    listFilter = filter;
8751
    MSG_DEBUG("listSourceFilter: " << listFilter);
227 andreas 8752
    return true;
8753
}
233 andreas 8754
 
8755
void TButton::setListViewColumns(int cols)
8756
{
8757
    DECL_TRACER("TButton::setListViewColumns(int cols)");
8758
 
8759
    if (cols <= 0)
8760
        return;
8761
 
8762
    tc = cols;
8763
}
8764
 
8765
void TButton::setListViewLayout(int layout)
8766
{
8767
    DECL_TRACER("TButton::setListViewLayout(int layout)");
8768
 
8769
    if (layout < 1 || layout > 6)
8770
        return;
8771
 
8772
    listLayout = layout;
8773
}
8774
 
8775
void TButton::setListViewComponent(int comp)
8776
{
8777
    DECL_TRACER("TButton::setListViewComponent(int comp)");
8778
 
8779
    if (comp < 0 || comp > 7)
8780
        return;
8781
 
8782
    listComponent = comp;
8783
}
8784
 
8785
void TButton::setListViewCellheight(int height, bool percent)
8786
{
8787
    DECL_TRACER("TButton::setListViewCellheight(int height, bool percent)");
8788
 
8789
    int minHeight = ht / tr;    // Total height / number of rows
8790
    int maxHeight = (int)((double)ht / 100.0 * 95.0);
8791
 
8792
    if (!percent && (height < minHeight || height > maxHeight))
8793
        return;
8794
 
8795
    if (percent)
8796
    {
8797
        int h = (int)((double)ht / 100.0 * (double)height);
8798
 
8799
        if (h >= minHeight && h <= maxHeight)
8800
            tj = h;
8801
 
8802
        return;
8803
    }
8804
 
8805
    tj = height;
8806
}
8807
 
8808
void TButton::setListViewFilterHeight(int height, bool percent)
8809
{
8810
    DECL_TRACER("TButton::setListViewFilterHeight(int height, bool percent)");
8811
 
8812
    if (percent && (height < 5 || height > 25))
8813
        return;
8814
 
8815
    if (!percent && height < 24)
8816
        return;
8817
 
8818
    if (percent)
8819
    {
8820
        listViewColFilterHeight = (int)((double)ht / 100.0 * (double)height);
8821
        return;
8822
    }
8823
    else
8824
    {
8825
        int maxHeight = (int)((double)ht / 100.0 * 25.0);
8826
 
8827
        if (height < maxHeight)
8828
            listViewColFilterHeight = height;
8829
    }
8830
}
8831
 
8832
void TButton::setListViewP1(int p1)
8833
{
8834
    DECL_TRACER("TButton::setListViewP1(int p1)");
8835
 
8836
    if (p1 < 10 || p1 > 90)
8837
        return;
8838
 
8839
    listViewP1 = p1;
8840
}
8841
 
8842
void TButton::setListViewP2(int p2)
8843
{
8844
    DECL_TRACER("TButton::setListViewP2(int p2)");
8845
 
8846
    if (p2 < 10 || p2 > 90)
8847
        return;
8848
 
8849
    listViewP2 = p2;
8850
}
8851
 
8852
void TButton::listViewNavigate(const string &command, bool select)
8853
{
8854
    DECL_TRACER("TButton::listViewNavigate(const string &command, bool select)");
8855
 
8856
    string cmd = command;
8857
    string upCmd = toUpper(cmd);
8858
 
8859
    if (upCmd != "T" && upCmd != "B" && upCmd != "D" && upCmd != "U" && !isNumeric(upCmd, true))
8860
        return;
8861
 
8862
    // TODO: Add code to navigate a list
8863
    MSG_WARNING("ListView navigation is not supported!" << " [" << upCmd << ", " << (select ? "TRUE" : "FALSE") << "]");
8864
}
8865
 
8866
void TButton::listViewRefresh(int interval, bool force)
8867
{
8868
    DECL_TRACER("TButton::listViewRefresh(int interval, bool force)");
8869
 
238 andreas 8870
    Q_UNUSED(interval);
8871
    Q_UNUSED(force);
8872
 
233 andreas 8873
    // TODO: Add code to load list data and display / refresh them
8874
}
8875
 
8876
void TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)
8877
{
8878
    DECL_TRACER("TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)");
8879
 
238 andreas 8880
    Q_UNUSED(columns);
8881
    Q_UNUSED(order);
8882
    Q_UNUSED(override);
8883
 
233 andreas 8884
    // TODO: Insert code to sort the data in the list
8885
}