Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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