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