Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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