Rev 482 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020 to 2025 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string>
#include <memory>
#include <algorithm>
#include <codecvt>
#include <fstream>
#include <unistd.h>
#include <include/core/SkPixmap.h>
#include <include/core/SkSize.h>
#include <include/core/SkColor.h>
#include <include/core/SkFont.h>
#include <include/core/SkTypeface.h>
#include <include/core/SkFontMetrics.h>
#include <include/core/SkTextBlob.h>
#include <include/core/SkRegion.h>
#include <include/core/SkImageFilter.h>
#include <include/effects/SkImageFilters.h>
#include <include/core/SkPath.h>
#include <include/core/SkSurfaceProps.h>
//#ifndef __MACH__
//#include <include/core/SkFilterQuality.h>
//#endif
#include <include/core/SkMaskFilter.h>
//#include <include/core/SkImageEncoder.h>
#include <include/core/SkRRect.h>
#include <include/core/SkBlurTypes.h>
//#ifdef __ANDROID__
//#include <QtAndroidExtras/QAndroidJniObject>
//#include <QtAndroid>
//#endif
#include "tbutton.h"
#include "tbuttonstates.h"
#include "thttpclient.h"
#include "terror.h"
#include "tconfig.h"
#include "tresources.h"
#include "ticons.h"
#include "tamxnet.h"
#include "tpagemanager.h"
#include "tsystemsound.h"
#include "timgcache.h"
#include "turl.h"
#include "tlock.h"
#include "ttpinit.h"
#include "tlauncher.h"
#if TESTMODE == 1
#include "testmode.h"
#endif
#if __cplusplus < 201402L
# error "This module requires at least C++14 standard!"
#else
# if __cplusplus < 201703L
# include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
# warning "Support for C++14 and experimental filesystem will be removed in a future version!"
# else
# include <filesystem>
# ifdef __ANDROID__
namespace fs = std::__fs::filesystem;
# else
namespace fs = std::filesystem;
# endif
# endif
#endif
using std::exception;
using std::string;
using std::vector;
using std::unique_ptr;
using std::map;
using std::pair;
using std::min;
using std::max;
using std::thread;
using std::atomic;
using std::mutex;
using std::bind;
using namespace Button;
using namespace Expat;
#define MAX_BUFFER 65536
#define RLOG_INFO 0x00fe
#define RLOG_WARNING 0x00fd
#define RLOG_ERROR 0x00fb
#define RLOG_TRACE 0x00f7
#define RLOG_DEBUG 0x00ef
#define RLOG_PROTOCOL 0x00f8
#define RLOG_ALL 0x00e0
extern TIcons *gIcons;
extern amx::TAmxNet *gAmxNet;
extern TPageManager *gPageManager;
THR_REFRESH_t *TButton::mThrRefresh = nullptr;
vector<BITMAP_CACHE> nBitmapCache; // Holds the images who are delayed because they are external
SYSTEF_t sysTefs[] = {
{ 1, "Outline-S" },
{ 2, "Outline-M" },
{ 3, "Outline-L" },
{ 4, "Outline-X" },
{ 5, "Glow-S" },
{ 6, "Glow-M" },
{ 7, "Glow-L" },
{ 8, "Glow-X" },
{ 9, "Soft Drop Shadow 1" },
{ 10, "Soft Drop Shadow 2" },
{ 11, "Soft Drop Shadow 3" },
{ 12, "Soft Drop Shadow 4" },
{ 13, "Soft Drop Shadow 5" },
{ 14, "Soft Drop Shadow 6" },
{ 15, "Soft Drop Shadow 7" },
{ 16, "Soft Drop Shadow 8" },
{ 17, "Medium Drop Shadow 1" },
{ 18, "Medium Drop Shadow 2" },
{ 19, "Medium Drop Shadow 3" },
{ 20, "Medium Drop Shadow 4" },
{ 21, "Medium Drop Shadow 5" },
{ 22, "Medium Drop Shadow 6" },
{ 23, "Medium Drop Shadow 7" },
{ 24, "Medium Drop Shadow 8" },
{ 25, "Hard Drop Shadow 1" },
{ 26, "Hard Drop Shadow 2" },
{ 27, "Hard Drop Shadow 3" },
{ 28, "Hard Drop Shadow 4" },
{ 29, "Hard Drop Shadow 5" },
{ 30, "Hard Drop Shadow 6" },
{ 31, "Hard Drop Shadow 7" },
{ 32, "Hard Drop Shadow 8" },
{ 33, "Soft Drop Shadow 1 with outline" },
{ 34, "Soft Drop Shadow 2 with outline" },
{ 35, "Soft Drop Shadow 3 with outline" },
{ 36, "Soft Drop Shadow 4 with outline" },
{ 37, "Soft Drop Shadow 5 with outline" },
{ 38, "Soft Drop Shadow 6 with outline" },
{ 39, "Soft Drop Shadow 7 with outline" },
{ 40, "Soft Drop Shadow 8 with outline" },
{ 41, "Medium Drop Shadow 1 with outline" },
{ 42, "Medium Drop Shadow 2 with outline" },
{ 43, "Medium Drop Shadow 3 with outline" },
{ 44, "Medium Drop Shadow 4 with outline" },
{ 45, "Medium Drop Shadow 5 with outline" },
{ 46, "Medium Drop Shadow 6 with outline" },
{ 47, "Medium Drop Shadow 7 with outline" },
{ 48, "Medium Drop Shadow 8 with outline" },
{ 49, "Hard Drop Shadow 1 with outline" },
{ 50, "Hard Drop Shadow 2 with outline" },
{ 51, "Hard Drop Shadow 3 with outline" },
{ 52, "Hard Drop Shadow 4 with outline" },
{ 53, "Hard Drop Shadow 5 with outline" },
{ 54, "Hard Drop Shadow 6 with outline" },
{ 55, "Hard Drop Shadow 7 with outline" },
{ 56, "Hard Drop Shadow 8 with outline" },
{ 0, "\0" }
};
TButton::TButton()
{
DECL_TRACER("TButton::TButton()");
mAniRunning = false;
mLastBlink.clear();
}
TButton::~TButton()
{
DECL_TRACER("TButton::~TButton()");
if (ap == 0 && ad == 8)
{
if (gAmxNet)
gAmxNet->deregNetworkState(mHandle);
}
if (ap == 0 && ((ad >= 141 && ad <= 143) || (ad >= 151 && ad <= 158)))
{
if (gAmxNet)
gAmxNet->deregTimer(mHandle);
}
if (ap == 0 && ad == 81) // Network state multi bargraph
{
if (gPageManager)
gPageManager->unregCallbackNetState(mHandle);
}
if (mTimer)
{
mTimer->stop();
while (mTimer->isRunning())
usleep(50);
delete mTimer;
}
if (mAniRunning)
{
ulong tm = nu * ru + nd * rd;
mAniStop = true;
while (mAniRunning)
std::this_thread::sleep_for(std::chrono::milliseconds(tm * 100));
}
THR_REFRESH_t *next, *p = mThrRefresh;
while (p)
{
if (p->mImageRefresh)
{
p->mImageRefresh->stop();
int counter = 0;
while (counter < 1000 && p->mImageRefresh->isRunning())
{
usleep(50);
counter++;
}
delete p->mImageRefresh;
p->mImageRefresh = nullptr;
}
next = p->next;
delete p;
p = next;
}
mThrRefresh = nullptr;
}
size_t TButton::initialize(TExpat *xml, size_t index)
{
DECL_TRACER("TButton::initialize(TExpat *xml, size_t index)");
if (!xml || index == TExpat::npos)
{
MSG_ERROR("Invalid NULL parameter passed!");
TError::setError();
return TExpat::npos;
}
vector<string> guestures = { "ga", "gu", "gd", "gr", "gl", "gt", "tu", "td", "tr", "tl" };
mChanged = true;
int lastLevel = 0;
int lastJoyX = 0;
int lastJoyY = 0;
vector<ATTRIBUTE_t> attrs = xml->getAttributes(index);
string stype = xml->getAttribute("type", attrs);
type = getButtonType(stype);
MSG_DEBUG("Button type: " << stype << " --> " << type);
string ename, content;
size_t oldIndex = index;
while((index = xml->getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
{
MSG_DEBUG("Element: " << ename << " at index " << index);
if (ename.compare("bi") == 0) // Button index
{
bi = xml->convertElementToInt(content);
MSG_DEBUG("Processing button index: " << bi);
}
else if (ename.compare("na") == 0) // Name
na = content;
else if (ename.compare("bd") == 0) // Description
bd = content;
else if (ename.compare("lt") == 0) // Left
{
lt = xml->convertElementToInt(content);
mPosLeft = lt;
}
else if (ename.compare("tp") == 0) // Top
{
tp = xml->convertElementToInt(content);
mPosTop = tp;
}
else if (ename.compare("wt") == 0) // Width
{
wt = xml->convertElementToInt(content);
mWidthOrig = wt;
}
else if (ename.compare("ht") == 0) // Height
{
ht = xml->convertElementToInt(content);
mHeightOrig = ht;
}
else if (ename.compare("zo") == 0) // Z-Order
zo = xml->convertElementToInt(content);
else if (ename.compare("hs") == 0) // bounding
hs = content;
else if (ename.compare("bs") == 0) // border style
bs = content;
else if (ename.compare("fb") == 0) // feedback type
fb = getButtonFeedback(content);
else if (ename.compare("ap") == 0) // address port
ap = xml->convertElementToInt(content);
else if (ename.compare("ad") == 0) // address code
ad = xml->convertElementToInt(content);
else if (ename.compare("ch") == 0) // channel code
ch = xml->convertElementToInt(content);
else if (ename.compare("cp") == 0) // channel port
cp = xml->convertElementToInt(content);
else if (ename.compare("lp") == 0) // level port
lp = xml->convertElementToInt(content);
else if (ename.compare("lv") == 0) // level code
lv = xml->convertElementToInt(content);
else if (ename.compare("dr") == 0) // level direction
dr = content;
else if (ename.compare("co") == 0) // command port
co = xml->convertElementToInt(content);
else if (ename.compare("cm") == 0) // commands to send on button hit
cm.push_back(content);
else if (ename.compare("va") == 0) // Level control value
va = xml->convertElementToInt(content);
else if (ename.compare("rm") == 0) // State count
rm = xml->convertElementToInt(content);
else if (ename.compare("nu") == 0) // Animate time up
nu = xml->convertElementToInt(content);
else if (ename.compare("nd") == 0) // Animate time down
nd = xml->convertElementToInt(content);
else if (ename.compare("ar") == 0) // Auto repeat state
ar = xml->convertElementToInt(content);
else if (ename.compare("ru") == 0) // Animate time up (bargraph on click)
ru = xml->convertElementToInt(content);
else if (ename.compare("rd") == 0) // Animate time down (bargraph on click)
rd = xml->convertElementToInt(content);
else if (ename.compare("lu") == 0) // Animate time up (bargraph)
lu = xml->convertElementToInt(content);
else if (ename.compare("ld") == 0) // Animate time down (bargraph)
ld = xml->convertElementToInt(content);
else if (ename.compare("rv") == 0) // Level control repeat time
rv = xml->convertElementToInt(content);
else if (ename.compare("rl") == 0) // Bargraph range low
rl = xml->convertElementToInt(content);
else if (ename.compare("rh") == 0) // Bargraph range high
rh = xml->convertElementToInt(content);
else if (ename.compare("ri") == 0) // Bargraph inverted (0 = normal, 1 = inverted)
{
ri = xml->convertElementToInt(content);
if (ri > 0 && lf != "center" && lf != "dragCenter")
{
lastLevel = rh - rl;
lastJoyX = lastLevel;
}
}
else if (ename.compare("ji") == 0) // Joystick aux inverted (0 = normal, 1 = inverted)
{
ji = xml->convertElementToInt(content);
if (ji > 0 && lf != "center" && lf != "dragCenter")
lastJoyY = rh - rl;
}
else if (ename.compare("rn") == 0) // Bargraph: Range drag increment
rn = xml->convertElementToInt(content);
else if (ename.compare("lf") == 0) // Bargraph function
lf = content;
else if (ename.compare("sd") == 0) // Name/Type of slider for a bargraph
sd = content;
else if (ename.compare("vt") == 0) // Level control type
vt = content;
else if (ename.compare("cd") == 0) // Name of cursor for a joystick
cd = content;
else if (ename.compare("sc") == 0) // Color of slider (for bargraph)
sc = content;
else if (ename.compare("cc") == 0) // Color of cursor (for joystick)
cc = content;
else if (ename.compare("mt") == 0) // Length of text area
mt = xml->convertElementToInt(content);
else if (ename.compare("dt") == 0) // Textarea multiple/single line
dt = content;
else if (ename.compare("im") == 0) // Input mask of a text area
im = content;
else if (ename.compare("so") == 0) // String output port
so = xml->convertElementToInt(content);
else if (ename.compare("op") == 0) // String the button send
op = content;
else if (ename.compare("pc") == 0) // Password character for text area (single line)
pc = content;
else if (ename.compare("pp") == 0) // Password protection
pp = xml->convertElementToInt(content);
else if (ename.compare("ta") == 0) // Listbox table channel
ta = xml->convertElementToInt(content);
else if (ename.compare("ti") == 0) // Listbox table address channel of rows
ti = xml->convertElementToInt(content);
else if (ename.compare("tr") == 0) // Listbox number of rows
tr = xml->convertElementToInt(content);
else if (ename.compare("tc") == 0) // Listbox number of columns
tc = xml->convertElementToInt(content);
else if (ename.compare("tj") == 0) // Listbox row height
tj = xml->convertElementToInt(content);
else if (ename.compare("tk") == 0) // Listbox preferred row height
tk = xml->convertElementToInt(content);
else if (ename.compare("of") == 0) // Listbox list offset: 0=disabled/1=enabled
of = xml->convertElementToInt(content);
else if (ename.compare("tg") == 0) // Listbox managed: 0=no/1=yes
tg = xml->convertElementToInt(content);
else if (ename.compare("st") == 0) // SubPageView index
st = xml->convertElementToInt(content);
else if (ename.compare("ws") == 0) // SubPageView: Wrap subpages; 1 = YES
ws = xml->convertElementToInt(content);
else if (ename.compare("sa") == 0) // SubPageView: Percent of space between items in list
sa = xml->convertElementToInt(content);
else if (ename.compare("dy") == 0) // SubPageView: Allow dynamic reordering; 1 = YES
dy = xml->convertElementToInt(content);
else if (ename.compare("rs") == 0) // SubPageView: Reset view on show; 1 = YES
rs = xml->convertElementToInt(content);
else if (ename.compare("on") == 0) // SubPageView direction
on = content;
else if (ename.compare("ba") == 0) // SubPageView scrollbar
ba = xml->convertElementToInt(content);
else if (ename.compare("bo") == 0) // SubPageView scrollbar offset
bo = xml->convertElementToInt(content);
else if (ename.compare("we") == 0) // SubViewPage Anchor position
we = content;
else if (ename.compare("hd") == 0) // 1 = Hidden, 0 = Normal visible
hd = xml->convertElementToInt(content);
else if (ename.compare("da") == 0) // 1 = Disabled, 0 = Normal active
da = xml->convertElementToInt(content);
else if (ename.compare("ac") == 0) // Direction of text (guess)
{
ac_di = xml->getAttributeInt("di", attrs); // 0 = left to right; 1 = right to left
}
else if (ename.compare("pf") == 0) // Function call TP4
{
PUSH_FUNC_T pf;
pf.pfName = content;
pf.pfType = xml->getAttribute("type", attrs);
pushFunc.push_back(pf);
}
else if ((ename.compare("ep") == 0 || ename.compare("er") == 0) && xml->isElementTypeStart(index)) // Function call TP5: Event on press/release
{
PUSH_FUNC_T pf;
pf.event = ename.compare("ep") == 0 ? EVENT_PRESS : EVENT_RELEASE;
string e;
while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
{
if (e.compare("pgFlip") == 0)
{
pf.action = BT_ACTION_PGFLIP;
pf.item = xml->getAttributeInt("item", attrs);
pf.pfType = xml->getAttribute("type", attrs);
pf.pfName = content;
pushFunc.push_back(pf);
}
else if (e.compare("launch") == 0)
{
pf.action = BT_ACTION_LAUNCH;
pf.item = xml->getAttributeInt("item", attrs);
pf.ID = xml->getAttributeInt("id", attrs);
pf.pfAction = xml->getAttribute("action", attrs);
pf.pfName = content;
pushFunc.push_back(pf);
}
oldIndex = index;
}
index = oldIndex + 1;
}
else if (isButtonEvent(ename, guestures) && xml->isElementTypeStart(index)) // Fuction call TP5: Event on guesture
{
PUSH_FUNC pf;
pf.event = getButtonEvent(ename);
string e;
while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
{
if (e.compare("pgFlip") == 0)
{
pf.action = BT_ACTION_PGFLIP;
pf.item = xml->getAttributeInt("item", attrs);
pf.pfType = xml->getAttribute("type", attrs);
pf.pfName = content;
pushFunc.push_back(pf);
}
else if (e.compare("launch") == 0)
{
pf.action = BT_ACTION_LAUNCH;
pf.item = xml->getAttributeInt("item", attrs);
pf.ID = xml->getAttributeInt("id", attrs);
pf.pfAction = xml->getAttribute("action", attrs);
pf.pfName = content;
pushFunc.push_back(pf);
}
oldIndex = index;
}
index = oldIndex + 1;
}
else if (ename.compare("sr") == 0) // Section state resources
{
SR_T bsr;
bsr.number = xml->getAttributeInt("number", attrs); // State number
MSG_DEBUG("Button: " << na << ": State element: " << bsr.number);
string e;
while ((index = xml->getNextElementFromIndex(index, &e, &content, &attrs)) != TExpat::npos)
{
if (e.compare("do") == 0) // Draw order
bsr._do = content;
else if (e.compare("bs") == 0) // Frame type
bsr.bs = content;
else if (e.compare("mi") == 0) // Chameleon image
bsr.mi = content;
else if (e.compare("cb") == 0) // Border color
bsr.cb = content;
else if (e.compare("cf") == 0) // Fill color
bsr.cf = content;
else if (e.compare("ct") == 0) // Text color
bsr.ct = content;
else if (e.compare("ec") == 0) // Text effect color
bsr.ec = content;
else if (e.compare("bm") == 0) // Bitmap image
{
bsr.bm = content;
bsr.dynamic = ((xml->getAttributeInt("dynamic", attrs) == 1) ? true : false);
}
else if (e.compare("bitmapEntry") == 0) // G5 start of bitmap table
{
string fname;
BITMAPS_t bitmapEntry;
MSG_DEBUG("Section: " << e);
while ((index = xml->getNextElementFromIndex(index, &fname, &content, &attrs)) != TExpat::npos)
{
if (fname.compare("fileName") == 0)
bitmapEntry.fileName = content;
else if (fname.compare("justification") == 0)
bitmapEntry.justification = static_cast<ORIENTATION>(xml->convertElementToInt(content));
else if (fname.compare("offsetX") == 0)
bitmapEntry.offsetX = xml->convertElementToInt(content);
else if (fname.compare("offsetY") == 0)
bitmapEntry.offsetY = xml->convertElementToInt(content);
oldIndex = index;
}
bsr.bitmaps.push_back(bitmapEntry);
if (index == TExpat::npos)
index = oldIndex + 1;
}
else if (e.compare("sd") == 0) // Sound file
bsr.sd = content;
else if (e.compare("sb") == 0) // index external graphic
bsr.sb = xml->convertElementToInt(content);
else if (e.compare("ii") == 0) // Icon index
bsr.ii = xml->convertElementToInt(content);
else if (e.compare("ji") == 0) // Icon/bitmap orientation
bsr.ji = xml->convertElementToInt(content);
else if (e.compare("jb") == 0) // Bitmap orientation
bsr.jb = xml->convertElementToInt(content);
else if (e.compare("bx") == 0) // Absolute image position X
bsr.bx = xml->convertElementToInt(content);
else if (e.compare("by") == 0) // Absolute image position Y
bsr.by = xml->convertElementToInt(content);
else if (e.compare("ix") == 0) // Absolute Icon position X
bsr.ix = xml->convertElementToInt(content);
else if (e.compare("iy") == 0) // Absolute Icon position Y
bsr.iy = xml->convertElementToInt(content);
else if (e.compare("fi") == 0) // Font index
bsr.fi = xml->convertElementToInt(content);
else if (e.compare("te") == 0) // Text
bsr.te = content;
else if (e.compare("ff") == 0) // G5 font file name
bsr.ff = content;
else if (e.compare("fs") == 0) // G5 font size
bsr.fs = xml->convertElementToInt(content);
else if (e.compare("jt") == 0) // Text orientation
bsr.jt = (ORIENTATION)xml->convertElementToInt(content);
else if (e.compare("tx") == 0) // Absolute text position X
bsr.tx = xml->convertElementToInt(content);
else if (e.compare("ty") == 0) // Absolute text position Y
bsr.ty = xml->convertElementToInt(content);
else if (e.compare("ww") == 0) // Word wrap
bsr.ww = xml->convertElementToInt(content);
else if (e.compare("et") == 0) // Text effects
bsr.et = xml->convertElementToInt(content);
else if (e.compare("oo") == 0) // Opacity
bsr.oo = xml->convertElementToInt(content);
else if (e.compare("md") == 0) // Marquee type
bsr.md = xml->convertElementToInt(content);
else if (e.compare("mr") == 0) // Marquee enable/disable
bsr.mr = xml->convertElementToInt(content);
oldIndex = index;
}
sr.push_back(bsr);
if (index == TExpat::npos)
index = oldIndex + 1;
}
if (index == TExpat::npos)
index = oldIndex + 1;
else if (index > oldIndex)
oldIndex = index;
}
MSG_DEBUG("Index after loop: " << (index == TExpat::npos ? 0 : index) << ", old index: " << oldIndex);
visible = !hd; // set the initial visibility
if (gPageManager)
{
TButtonStates *pbs = gPageManager->addButtonState(type, ap, ad, ch, cp, lp, lv);
if (!pbs)
{
MSG_ERROR("States of actual button " << bi << " (" << na << ") are not found!");
}
else
{
mButtonID = pbs->getID();
MSG_DEBUG("Button ID: " << getButtonIDstr() << ", type: " << buttonTypeToString() << ", index: " << bi << ", name: " << na);
pbs->setLastLevel(lastLevel);
pbs->setLastJoyX(lastJoyX);
pbs->setLastJoyY(lastJoyY);
}
}
/*
if (sr.size() > 0 && TStreamError::checkFilter(HLOG_DEBUG))
{
MSG_DEBUG("bi : " << bi);
MSG_DEBUG("na : " << na);
MSG_DEBUG("type: " << type);
MSG_DEBUG("lt : " << lt);
MSG_DEBUG("tp : " << tp);
MSG_DEBUG("wt : " << wt);
MSG_DEBUG("ht : " << ht);
vector<SR_T>::iterator iter;
size_t pos = 1;
for (iter = sr.begin(); iter != sr.end(); ++iter)
{
MSG_DEBUG(" " << pos << ": id: " << iter->number);
MSG_DEBUG(" " << pos << ": bs: " << iter->bs);
MSG_DEBUG(" " << pos << ": cb: " << iter->cb);
MSG_DEBUG(" " << pos << ": cf: " << iter->cf);
MSG_DEBUG(" " << pos << ": ct: " << iter->ct);
MSG_DEBUG(" " << pos << ": ec: " << iter->ec);
MSG_DEBUG(" " << pos << ": bm: " << iter->bm);
MSG_DEBUG(" " << pos << ": mi: " << iter->mi);
MSG_DEBUG(" " << pos << ": fi: " << iter->fi);
MSG_DEBUG(" " << pos << ": te: " << iter->te);
pos++;
}
}
*/
MSG_DEBUG("Added button " << bi << " --> " << na);
if (index == TExpat::npos)
index = oldIndex + 1;
MSG_DEBUG("Returning index " << index);
return index;
}
bool TButton::createSoftButton(const EXTBUTTON_t& bt)
{
DECL_TRACER("TButton::createSoftButton(const EXTBUTTON_t& bt)");
if (bt.sr.size() < 2)
{
MSG_ERROR("Button " << bt.bi << ": " << bt.na << " has less than 2 states!");
return false;
}
MSG_DEBUG("Adding soft button " << bt.bi << ": " << bt.na);
type = bt.type;
bi = bt.bi;
na = bt.na;
lt = mPosLeft = bt.lt;
tp = mPosTop = bt.tp;
wt = bt.wt;
ht = bt.ht;
zo = bt.zo;
hs = bt.hs;
bs = bt.bs;
fb = bt.fb;
ap = bt.ap;
ad = bt.ad;
lp = bt.lp;
lv = bt.lv;
dr = bt.dr;
lu = bt.lu;
ld = bt.ld;
rl = bt.rl;
rh = bt.rh;
rn = bt.rn;
sc = bt.sc;
sr = bt.sr;
if (gPageManager)
gPageManager->addButtonState(type, ap, ad, ch, cp, lp, lv);
mChanged = true;
return true;
}
BITMAP_t TButton::getLastImage()
{
DECL_TRACER("TButton::getLastImage()");
if (mLastImage.empty())
{
makeElement(mActInstance);
if (mLastImage.empty())
return BITMAP_t();
}
BITMAP_t image;
image.buffer = (unsigned char *)mLastImage.getPixels();
image.rowBytes = mLastImage.info().minRowBytes();
image.width = mLastImage.info().width();
image.height = mLastImage.info().height();
return image;
}
TBitmap TButton::getLastBitmap()
{
DECL_TRACER("TButton::getLastBitmap()");
if (mLastImage.empty())
makeElement(mActInstance);
TBitmap bitmap((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
return bitmap;
}
FONT_T TButton::getFont()
{
DECL_TRACER("TButton::getFont()");
if (!mFonts)
{
MSG_ERROR("No fonts available!");
return FONT_T();
}
if (type == LISTBOX && _getGlobalSettings)
{
_getGlobalSettings(this);
mActInstance = 0;
}
return mFonts->getFont(sr[mActInstance].fi);
}
FONT_STYLE TButton::getFontStyle()
{
DECL_TRACER("TButton::getFontStyle()");
if (!mFonts)
{
MSG_ERROR("No fonts available!");
return FONT_NONE;
}
return mFonts->getStyle(sr[mActInstance].fi);
}
void TButton::setBargraphLevel(int level)
{
DECL_TRACER("TButton::setBargraphLevel(int level)");
if (type != BARGRAPH && type != MULTISTATE_BARGRAPH && type != MULTISTATE_GENERAL)
return;
if (((type == BARGRAPH || type == MULTISTATE_BARGRAPH) && (level < rl || level > rh)) ||
(type == MULTISTATE_GENERAL && (level < 0 || (size_t)level >= sr.size())))
{
MSG_WARNING("Level for bargraph " << na << " is out of range! (" << rl << " to " << rh << " or size " << sr.size() << ")");
return;
}
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
TError::setError();
return;
}
int lastLevel = buttonStates->getLastLevel();
if (((type == BARGRAPH || type == MULTISTATE_BARGRAPH) && lastLevel != level) ||
(type == MULTISTATE_BARGRAPH && mActInstance != level))
mChanged = true;
if (!mChanged)
return;
if (type == BARGRAPH)
{
lastLevel = level;
buttonStates->setLastLevel(level);
drawBargraph(mActInstance, level);
}
else if (type == MULTISTATE_BARGRAPH)
{
lastLevel = level;
mActInstance = level;
buttonStates->setLastLevel(level);
drawMultistateBargraph(level);
}
else
setActive(level);
}
void TButton::moveBargraphLevel(int x, int y)
{
DECL_TRACER("TButton::moveBargraphLevel(int x, int y)");
if (type != BARGRAPH)
return;
if (lf.empty()) // Display only
return;
int level = 0;
int dragUp = false;
if (dr.compare("horizontal") == 0)
{
level = x;
level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(wt) * static_cast<double>(level));
}
else
{
level = ht - y;
level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(ht) * static_cast<double>(level));
}
if (lf == "drag" || lf == "dragCenter")
{
int diff = 0;
level += mBarThreshold;
if (dr == "horizontal")
dragUp = (mBarStartLevel > level);
else
dragUp = (level > mBarStartLevel);
diff = (mBarStartLevel > level ? (mBarStartLevel - level) : (level - mBarStartLevel));
double gap = static_cast<double>(rn) / static_cast<double>(rh - rl) * static_cast<double>(diff);
MSG_DEBUG("Gap is " << gap << ", diff: " << diff << ", mBarStartLevel: " << mBarStartLevel << ", level: " << level << ", rn: " << rn);
if (dragUp)
level = mBarStartLevel + static_cast<int>(gap);
else
level = mBarStartLevel - static_cast<int>(gap);
if (level < rl)
level = rl;
else if (level > rh)
level = rh;
}
drawBargraph(mActInstance, level, visible);
// Send the level
if (lp && lv && gPageManager && gPageManager->getLevelSendState() && gAmxNet)
{
gPageManager->sendLevel(lp, lv, (ri ? ((rh - rl) - level) : level));
TButtonStates *buttonStates = nullptr;
if ((buttonStates = getButtonState()) != nullptr)
buttonStates->setLastSendLevelX(level);
}
}
void TButton::sendJoystickLevels()
{
DECL_TRACER("TButton::sendJoystickLevels()");
if (type != JOYSTICK)
return;
if (!gAmxNet)
{
MSG_WARNING("The AMX communication thread is not initialized!");
return;
}
TButtonStates *buttonStates = getButtonState();
int lastJoyX = 0;
int lastJoyY = 0;
int lastSendLevelX = 0;
int lastSendLevelY = 0;
if (buttonStates)
{
lastJoyX = buttonStates->getLastJoyX();
lastJoyY = buttonStates->getLastJoyY();
lastSendLevelX = buttonStates->getLastSendLevelX();
lastSendLevelY = buttonStates->getLastSendLevelY();
}
else
{
MSG_ERROR("Button states not found!");
return;
}
// Send the levels
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
amx::ANET_SEND scmd;
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri ? ((rh - rl) - lastJoyX) : lastJoyX);
scmd.MC = 0x008a;
if (lastSendLevelX != scmd.value)
gAmxNet->sendCommand(scmd);
lastSendLevelX = scmd.value;
buttonStates->setLastSendLevelX(scmd.value);
scmd.channel = lv + 1;
scmd.level = lv + 1;
scmd.value = (ji ? ((rh - rl) - lastJoyY) : lastJoyY);
if (lastSendLevelY != scmd.value)
gAmxNet->sendCommand(scmd);
lastSendLevelY = scmd.value;
buttonStates->setLastSendLevelY(lastSendLevelY);
}
}
void TButton::sendBargraphLevel()
{
DECL_TRACER("TButton::sendBargraphLevel()");
if (type != BARGRAPH && type != MULTISTATE_BARGRAPH)
return;
if (!gAmxNet)
{
MSG_WARNING("The AMX communication thread is not initialized!");
return;
}
TButtonStates *buttonStates = getButtonState();
int lastLevel = 0;
int lastSendLevelX = 0;
if (buttonStates)
{
lastLevel = buttonStates->getLastLevel();
lastSendLevelX = buttonStates->getLastSendLevelX();
}
else
{
MSG_ERROR("Button states not found!");
return;
}
// Send the level
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
amx::ANET_SEND scmd;
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri ? ((rh - rl) - lastLevel) : lastLevel);
scmd.MC = 0x008a;
if (lastSendLevelX != lastLevel)
gAmxNet->sendCommand(scmd);
lastSendLevelX = lastLevel;
if (buttonStates)
buttonStates->setLastSendLevelX(lastSendLevelX);
}
}
bool TButton::invalidate()
{
DECL_TRACER("TButton::invalidate()");
if (prg_stopped)
return true;
ulong parent = mHandle & 0xffff0000;
THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
if (tr && tr->mImageRefresh)
{
if (tr->mImageRefresh->isRunning())
tr->mImageRefresh->stop();
}
if (type == TEXT_INPUT)
{
if (gPageManager && gPageManager->getCallDropButton())
gPageManager->getCallDropButton()(mHandle);
}
visible = false;
return true;
}
string& TButton::getDrawOrder(int instance)
{
DECL_TRACER("TButton::getDrawOrder(int instance)");
if (instance < 0 || (size_t)instance > sr.size())
{
MSG_ERROR("Instance is out of range!");
return dummy;
}
return sr[instance]._do;
}
BUTTONTYPE TButton::getButtonType(const string& bt)
{
DECL_TRACER("TButton::getButtonType(const string& bt)");
if (strCaseCompare(bt, "general") == 0)
return GENERAL;
else if (strCaseCompare(bt, "multi-state general") == 0 || strCaseCompare(bt, "multiGeneral") == 0)
return MULTISTATE_GENERAL;
else if (strCaseCompare(bt, "bargraph") == 0)
return BARGRAPH;
else if (strCaseCompare(bt, "multi-state bargraph") == 0 || strCaseCompare(bt, "multiBargraph") == 0)
return MULTISTATE_BARGRAPH;
else if (strCaseCompare(bt, "joystick") == 0)
return JOYSTICK;
else if (strCaseCompare(bt, "text input") == 0 || strCaseCompare(bt, "textArea") == 0)
return TEXT_INPUT;
else if (strCaseCompare(bt, "computer control") == 0)
return COMPUTER_CONTROL;
else if (strCaseCompare(bt, "take note") == 0)
return TAKE_NOTE;
else if (strCaseCompare(bt, "sub-page view") == 0 || strCaseCompare(bt, "subPageView") == 0)
return SUBPAGE_VIEW;
else if (strCaseCompare(bt, "listBox") == 0)
return LISTBOX;
return NONE;
}
string TButton::buttonTypeToString()
{
return buttonTypeToString(type);
}
string TButton::buttonTypeToString(BUTTONTYPE t)
{
switch(t)
{
case NONE: return "NONE";
case GENERAL: return "GENERAL";
case MULTISTATE_GENERAL: return "MULTISTAE GENERAL";
case BARGRAPH: return "BARGRAPH";
case MULTISTATE_BARGRAPH: return "MULTISTATE BARGRAPH";
case JOYSTICK: return "JOISTICK";
case TEXT_INPUT: return "TEXT INPUT";
case COMPUTER_CONTROL: return "COMPUTER CONTROL";
case TAKE_NOTE: return "TAKE NOTE";
case SUBPAGE_VIEW: return "SUBPAGE VIEW";
case LISTBOX: return "LISTBOX";
}
return "";
}
FEEDBACK TButton::getButtonFeedback(const string& fb)
{
DECL_TRACER("TButton::getButtonFeedback(const string& fb)");
if (fb.compare("channel") == 0)
return FB_CHANNEL;
else if (fb.compare("inverted channel") == 0)
return FB_INV_CHANNEL;
else if (fb.compare("always on") == 0)
return FB_ALWAYS_ON;
else if (fb.compare("momentary") == 0)
return FB_MOMENTARY;
else if (fb.compare("blink") == 0)
return FB_BLINK;
return FB_NONE;
}
bool TButton::createButtons(bool force)
{
DECL_TRACER("TButton::createButtons(bool force)");
if (prg_stopped)
return false;
if (force)
{
mChanged = true;
MSG_TRACE("Creating of image is forced!");
}
// Get the images, if there any
if (sr.empty())
return true;
vector<SR_T>::iterator srIter;
for (srIter = sr.begin(); srIter != sr.end(); ++srIter)
{
int number = srIter->number;
if (srIter->sb > 0)
continue;
if (!TTPInit::isTP5())
{
bool bmExistMi = false;
bool bmExistBm = false;
bool reload = false;
if (!srIter->mi.empty())
{
if ((bmExistMi = TImgCache::existBitmap(srIter->mi, _BMTYPE_CHAMELEON)) == false)
{
mChanged = true;
reload = true;
}
}
if (!srIter->bm.empty())
{
if ((bmExistBm = TImgCache::existBitmap(srIter->bm, _BMTYPE_BITMAP)) == false)
{
mChanged = true;
reload = true;
}
}
if (!force)
{
if (!reload) // If the image already exist, do not load it again.
continue;
}
if (!bmExistMi && !srIter->mi.empty()) // Do we have a chameleon image?
{
sk_sp<SkData> image;
SkBitmap bm;
if (!(image = readImage(srIter->mi)))
return false;
DecodeDataToBitmap(image, &bm);
if (bm.empty())
{
MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
return false;
}
TImgCache::addImage(srIter->mi, bm, _BMTYPE_CHAMELEON);
srIter->mi_width = bm.info().width();
srIter->mi_height = bm.info().height();
mChanged = true;
}
if (!bmExistBm && !srIter->bm.empty()) // Do we have a bitmap?
{
sk_sp<SkData> image;
SkBitmap bm;
if (!(image = readImage(srIter->bm)))
return false;
DecodeDataToBitmap(image, &bm);
if (bm.empty())
{
MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
return false;
}
TImgCache::addImage(srIter->bm, bm, _BMTYPE_BITMAP);
srIter->bm_width = bm.info().width();
srIter->bm_height = bm.info().height();
mChanged = true;
}
}
else
{
if (srIter->bitmaps.size() > 0)
{
vector<BITMAPS_t>::iterator bmIter;
sk_sp<SkData> image;
SkBitmap bm;
for (bmIter = srIter->bitmaps.begin(); bmIter != srIter->bitmaps.end(); ++bmIter)
{
if (force || !TImgCache::existBitmap(bmIter->fileName, _BMTYPE_BITMAP))
{
if (!(image = readImage(bmIter->fileName)))
{
MSG_WARNING("Error reading image " << bmIter->fileName << "!");
continue;
}
DecodeDataToBitmap(image, &bm);
if (bm.empty())
{
MSG_WARNING("Could not create a picture for element " << number << " on button " << bi << " (" << na << ")");
continue;
}
TImgCache::addImage(bmIter->fileName, bm, _BMTYPE_BITMAP);
bmIter->width = bm.info().width();
bmIter->height = bm.info().height();
mChanged = true;
}
}
}
}
}
return true;
}
void TButton::refresh()
{
DECL_TRACER("TButton::refresh()");
mChanged = true;
makeElement();
}
bool TButton::makeElement(int instance)
{
DECL_TRACER("TButton::makeElement(int instance)");
if (prg_stopped)
return false;
int inst = mActInstance;
if (instance >= 0 && static_cast<size_t>(instance) < sr.size())
{
if (mActInstance != instance)
mChanged = true;
inst = instance;
}
else if (inst < 0 || static_cast<size_t>(inst) >= sr.size())
inst = mActInstance = 0;
int lastLevel = 0;
int lastJoyX = 0;
int lastJoyY = 0;
TButtonStates *buttonStates = nullptr;
bool isSystem = isSystemButton();
if (type == BARGRAPH || type == JOYSTICK || type == MULTISTATE_BARGRAPH)
{
TButtonStates *buttonStates = getButtonState();
if (buttonStates)
{
lastLevel = buttonStates->getLastLevel();
lastJoyX = buttonStates->getLastJoyX();
lastJoyY = buttonStates->getLastJoyY();
MSG_DEBUG("lastLevel: " << lastLevel << ", lastJoyX: " << lastJoyX << ", lastJoyY: " << lastJoyY);
}
else
{
MSG_ERROR("Button states not found!");
return false;
}
}
if (type == MULTISTATE_GENERAL && ar == 1)
return drawButtonMultistateAni();
else if (type == BARGRAPH && isSystem && lv == 9) // System volume button
return drawBargraph(inst, TConfig::getSystemVolume());
else if (type == BARGRAPH)
{
if (lf == "center" || lf == "dragCenter")
lastLevel = (rh - rl) / 2;
return drawBargraph(inst, lastLevel);
}
else if (type == MULTISTATE_BARGRAPH)
return drawMultistateBargraph(lastLevel, true);
else if (type == TEXT_INPUT)
{
if (isSystem && !mSystemReg)
{
registerSystemButton();
mChanged = true;
}
drawTextArea(inst);
mActInstance = inst;
}
else if (type == LISTBOX)
{
if (_getListContent && !mSystemReg)
{
mListContent = _getListContent(mHandle, ap, ta, ti, tr, tc);
mChanged = true;
}
if (isSystem)
mSystemReg = true;
drawList();
}
else if (isSystem && type == GENERAL)
{
TConfig::setTemporary(true);
if (isSystemCheckBox(ch))
{
int in = getButtonInstance(0, ch);
if (in >= 0)
{
inst = mActInstance = in;
#ifndef __ANDROID__
if (ch == SYSTEM_ITEM_VIEWSCALEFIT && sr[0].oo < 0) // scale to fit disabled
{
sr[0].oo = 128;
mChanged = true;
}
#else
if (ch == SYSTEM_ITEM_VIEWBANNER && sr[0].oo < 0) // show banner disabled
{
sr[0].oo = 128;
mChanged = true;
}
#endif
if (ch == SYSTEM_ITEM_VIEWTOOLBAR) // Force toolbar is only available if toolbar is not suppressed
{
if (TConfig::getToolbarSuppress() && sr[0].oo < 0)
{
sr[0].oo = 128;
mChanged = true;
}
else if (!TConfig::getToolbarSuppress() && sr[0].oo > 0)
{
sr[0].oo = -1;
mChanged = true;
}
}
}
}
else if (isSystemTextLine(ad) && ad != SYSTEM_ITEM_FTPSURFACE)
{
sr[0].te = sr[1].te = fillButtonText(ad, 0);
mChanged = true;
}
TConfig::setTemporary(false);
if (mLastImage.empty())
mChanged = true;
MSG_DEBUG("Drawing system button " << ch << " with instance " << inst);
return drawButton(inst);
}
else if (type == JOYSTICK)
{
if (lf == "center" || lf == "dragCenter")
lastJoyX = lastJoyY = (rh - rl) / 2;
if (buttonStates)
{
buttonStates->setLastJoyX(lastJoyX);
buttonStates->setLastJoyY(lastJoyY);
}
return drawJoystick(lastJoyX, lastJoyY);
}
else
{
if (mLastImage.empty())
mChanged = true;
return drawButton(inst);
}
return false;
}
bool TButton::setActive(int instance)
{
DECL_TRACER("TButton::setActive(int instance)");
if (mAniRunning)
{
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " is out of range from 0 to " << sr.size() << "!");
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (instance == mActInstance && !mLastImage.empty())
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
mActInstance = instance;
mChanged = true;
makeElement(instance);
return true;
}
bool TButton::setIcon(int id, int instance)
{
DECL_TRACER("TButton::setIcon(int id, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].ii != id)
mChanged = true;
sr[inst].ii = id;
inst++;
}
return makeElement(instance);
}
bool TButton::setIcon(const string& icon, int instance)
{
DECL_TRACER("TButton::setIcon(const string& icon, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (!gIcons)
{
gIcons = new TIcons();
if (TError::isError())
{
MSG_ERROR("Error initializing icons!");
return false;
}
}
int id = gIcons->getNumber(icon);
if (id == -1)
{
MSG_WARNING("Icon " << icon << " not found!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].ii == id)
{
inst++;
continue;
}
if (sr[inst].ii != id)
mChanged = true;
sr[inst].ii = id;
inst++;
}
return makeElement(instance);
}
bool TButton::revokeIcon(int instance)
{
DECL_TRACER("TButton::revokeIcon(int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].ii == 0)
{
inst++;
continue;
}
if (sr[inst].ii != 0)
mChanged = true;
sr[inst].ii = 0;
inst++;
}
return makeElement(instance);
}
bool TButton::setText(const string& txt, int instance)
{
DECL_TRACER("TButton::setText(const string& txt, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
#if TESTMODE == 1
setAllDone();
#endif
return false;
}
if (!setTextOnly(txt, instance))
{
#if TESTMODE == 1
setAllDone();
#endif
return false;
}
if (!mChanged) // Do not try to redraw the button if nothing changed
{
#if TESTMODE == 1
MSG_INFO("Nothing changed!");
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
bool TButton::setTextOnly(const string& txt, int instance)
{
DECL_TRACER("TButton::setTextOnly(const string& txt, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
MSG_DEBUG("Setting text to: " << txt);
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].te != txt && static_cast<int>(i) == mActInstance)
mChanged = true;
sr[i].te = txt;
}
}
else
{
if (sr[instance].te != txt && static_cast<int>(instance) == mActInstance)
mChanged = true;
sr[instance].te = txt;
}
if (instance <= 0 && isSystemButton())
{
bool temp = TConfig::setTemporary(true);
// If we've an input line or the text line of a "combobox" then we'll
// save the changed value here.
switch(ad)
{
case SYSTEM_ITEM_NETLINX_IP: TConfig::saveController(txt); break;
case SYSTEM_ITEM_NETLINX_CHANNEL: TConfig::saveChannel(atoi(txt.c_str())); break;
case SYSTEM_ITEM_NETLINX_PORT: TConfig::savePort(atoi(txt.c_str())); break;
case SYSTEM_ITEM_NETLINX_PTYPE: TConfig::savePanelType(txt); break;
case SYSTEM_ITEM_SYSTEMSOUND: TConfig::saveSystemSoundFile(txt); break;
case SYSTEM_ITEM_SINGLEBEEP: TConfig::saveSingleBeepFile(txt); break;
case SYSTEM_ITEM_DOUBLEBEEP: TConfig::saveDoubleBeepFile(txt); break;
case SYSTEM_ITEM_SIPPROXY: TConfig::setSIPproxy(txt); break;
case SYSTEM_ITEM_SIPPORT: TConfig::setSIPport(atoi(txt.c_str())); break;
case SYSTEM_ITEM_SIPSTUN: TConfig::setSIPstun(txt); break;
case SYSTEM_ITEM_SIPDOMAIN: TConfig::setSIPdomain(txt); break;
case SYSTEM_ITEM_SIPUSER: TConfig::setSIPuser(txt); break;
case SYSTEM_ITEM_SIPPASSWORD: TConfig::setSIPpassword(txt); break;
case SYSTEM_ITEM_LOGLOGFILE: TConfig::saveLogFile(txt); break;
case SYSTEM_ITEM_FTPUSER: TConfig::saveFtpUser(txt); break;
case SYSTEM_ITEM_FTPPASSWORD: TConfig::saveFtpPassword(txt); break;
case SYSTEM_ITEM_FTPSURFACE: TConfig::saveFtpSurface(txt); break;
}
TConfig::setTemporary(temp);
}
return true;
}
bool TButton::appendText(const string &txt, int instance)
{
DECL_TRACER("TButton::appendText(const string &txt, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (txt.empty())
{
#if TESTMODE == 1
__success = true;
__done = true;
#endif
return true;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
sr[i].te.append(txt);
}
else
sr[instance].te.append(txt);
mChanged = true;
return makeElement(instance);
}
void TButton::setTextCursorPosition(int oldPos, int newPos)
{
DECL_TRACER("TButton::setTextCursorPosition(int oldPos, int newPos)");
if (type != TEXT_INPUT)
return;
if (oldPos == newPos && newPos == mCursorPosition)
return;
mCursorPosition = newPos;
}
void TButton::setTextFocus(bool in)
{
DECL_TRACER("TButton::setTextFocus(bool in)");
if (type != TEXT_INPUT)
return;
mHasFocus = in;
if (mHasFocus && mActInstance != STATE_ON)
makeElement(STATE_ON);
else if (!mHasFocus && mActInstance != STATE_OFF)
makeElement(STATE_OFF);
}
bool TButton::setBorderColor(const string &color, int instance)
{
DECL_TRACER("TButton::setBorderColor(const string &color, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].cb.compare(color) == 0)
continue;
if ((int)i == mActInstance)
mChanged = true;
sr[i].cb = color;
}
}
else if (sr[instance].cb != color)
{
if (mActInstance != instance)
mChanged = true;
sr[instance].cb = color;
}
if (!mChanged)
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
string TButton::getBorderColor(int instance)
{
DECL_TRACER("TButton::getBorderColor(int instance)");
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return string();
}
return sr[instance].cb;
}
bool TButton::setFillColor(const string& color, int instance)
{
DECL_TRACER("TButton::setFillColor(const string& color, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].cf == color)
continue;
if ((int)i == mActInstance)
mChanged = true;
sr[i].cf = color;
}
}
else if (sr[instance].cf != color)
{
if (mActInstance != instance)
mChanged = true;
sr[instance].cf = color;
}
if (!mChanged)
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
bool TButton::setTextColor(const string& color, int instance)
{
DECL_TRACER("TButton::setTextColor(const string& color, int instance)");
if (!setTextColorOnly(color, instance))
return false;
if (!mChanged)
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
bool TButton::setTextColorOnly(const string& color, int instance)
{
DECL_TRACER("TButton::setTextColorOnly(const string& color, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].ct == color)
continue;
if ((int)i == mActInstance)
mChanged = true;
sr[i].ct = color;
}
}
else if (sr[instance].ct != color)
{
if (mActInstance == instance)
mChanged = true;
sr[instance].ct = color;
}
return true;
}
bool TButton::setDrawOrder(const string& order, int instance)
{
DECL_TRACER("TButton::setDrawOrder(const string& order, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i]._do == order)
continue;
if ((int)i == mActInstance)
mChanged = true;
sr[i]._do = order;
}
}
else if (sr[instance]._do != order)
{
if (mActInstance == instance)
mChanged = true;
sr[instance]._do = order;
}
if (!mChanged)
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
FEEDBACK TButton::getFeedback()
{
DECL_TRACER("TButton::getFeedback()");
if (type != GENERAL)
return FB_NONE;
return fb;
}
bool TButton::setFeedback(FEEDBACK feedback)
{
DECL_TRACER("TButton::setFeedback(FEEDBACK feedback)");
if (type != GENERAL)
{
#if TESTMODE == 1
setAllDone();
#endif
return false;
}
int oldFB = fb;
fb = feedback;
if (mEnabled && !hd)
{
if ((feedback == FB_ALWAYS_ON || feedback == FB_INV_CHANNEL) && mActInstance != 1)
{
mActInstance = 1;
mChanged = true;
makeElement(1);
}
else if (oldFB == FB_ALWAYS_ON && feedback != FB_ALWAYS_ON && feedback != FB_INV_CHANNEL && mActInstance == 1)
{
mActInstance = 0;
mChanged = true;
makeElement(0);
}
}
#if TESTMODE == 1
if (!mChanged)
__success = true;
setScreenDone();
#endif
return true;
}
bool TButton::setBorderStyle(const string& style, int instance)
{
DECL_TRACER("TButton::setBorderStyle(const string& style, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
mChanged = true;
MSG_DEBUG("Setting border " << style);
if (strCaseCompare(style, "None") == 0) // Clear the border?
{
if (instance < 0)
{
bs.clear();
for (size_t i = 0; i < sr.size(); ++i)
sr[i].bs.clear();
}
else
{
sr[instance].bs.clear();
bs.clear();
}
if (mEnabled && !hd)
makeElement(instance);
return true;
}
// Look in the system table and try to find the border.
if (gPageManager && gPageManager->getSystemDraw())
{
if (gPageManager->getSystemDraw()->existBorder(style))
{
if (instance < 0)
{
bs = style;
for (size_t i = 0; i < sr.size(); ++i)
sr[i].bs = style;
}
else
{
sr[instance].bs = style;
if (bs != style)
bs.clear();
}
if (mEnabled && !hd)
makeElement(instance);
return true;
}
}
// Check whether it is a supported style or not. If the style is not
// supported, it will be ignored.
string corrName = getCorrectName(style);
if (!style.empty())
{
if (instance < 0)
{
bs = corrName;
for (size_t i = 0; i < sr.size(); ++i)
sr[i].bs = corrName;
}
else
{
sr[instance].bs = corrName;
if (bs != corrName)
bs.clear();
}
if (mEnabled && !hd)
makeElement(instance);
return true;
}
#if TESTMODE == 1
__done = true;
#endif
return false;
}
bool TButton::setBorderStyle(int style, int instance)
{
DECL_TRACER("TButton::setBorderStyle(int style, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (style == 0) // Clear the border?
{
if (instance < 0)
{
bs.clear();
for (size_t i = 0; i < sr.size(); ++i)
{
if (!sr[i].bs.empty())
mChanged = true;
sr[i].bs.clear();
}
if (!bs.empty())
mChanged = true;
bs.clear();
}
else
{
if (!sr[instance].bs.empty())
mChanged = true;
sr[instance].bs.clear();
bs.clear();
}
if (mEnabled && !hd)
makeElement(instance);
return true;
}
string st = getBorderName(style);
if (st.empty())
{
MSG_WARNING("The index " << style << " is not supported!");
#if TESTMODE == 1
setAllDone();
#endif
return false;
}
// Look in the system table and try to find the border.
if (gPageManager && gPageManager->getSystemDraw())
{
if (gPageManager->getSystemDraw()->existBorder(st))
{
MSG_DEBUG("Found frame " << st << " and draw it ...");
if (instance < 0)
{
bs = st;
for (size_t i = 0; i < sr.size(); ++i)
sr[i].bs = st;
}
else
{
sr[instance].bs = st;
if (bs != st)
bs.clear();
}
mChanged = true;
if (mEnabled && !hd)
makeElement(instance);
return true;
}
}
// Check whether it is a supported style or not. If the style is not
// supported, it will be ignored.
if (instance < 0)
{
bs = st;
for (size_t i = 0; i < sr.size(); ++i)
sr[i].bs = st;
}
else
{
sr[instance].bs = st;
if (bs != st)
bs.clear();
}
mChanged = true;
if (mEnabled && !hd)
makeElement(instance);
return true;
}
string TButton::getBorderStyle(int instance)
{
DECL_TRACER("TButton::getBorderStyle(int instance)");
if (instance < 0 || instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << (instance + 1) << " submitted!");
return string();
}
if (sr[instance].bs.empty())
return bs;
return sr[instance].bs;
}
bool TButton::setBargraphUpperLimit(int limit)
{
DECL_TRACER("TButton::setBargraphUpperLimit(int limit)");
if (limit < 1 || limit > 65535)
{
MSG_ERROR("Invalid upper limit " << limit);
return false;
}
rh = limit;
return true;
}
bool TButton::setBargraphLowerLimit(int limit)
{
DECL_TRACER("TButton::setBargraphLowerLimit(int limit)");
if (limit < 1 || limit > 65535)
{
MSG_ERROR("Invalid lower limit " << limit);
return false;
}
rl = limit;
return true;
}
bool TButton::setBargraphSliderColor(const string& color)
{
DECL_TRACER("TButton::setBargraphSliderColor(const string& color, int inst)");
if (!TColor::isValidAMXcolor(color))
{
MSG_PROTOCOL("Invalid color >" << color << "< ignored!");
return false;
}
if (type == BARGRAPH && sc != color)
{
mChanged = true;
sc = color;
}
else if (type == JOYSTICK && cc != color)
{
mChanged = true;
cc = color;
}
if (mChanged && visible)
refresh();
return true;
}
/*
* Change the bargraph slider name or joystick cursor name.
*/
bool TButton::setBargraphSliderName(const string& name)
{
DECL_TRACER("TButton::setBargraphSliderName(const string& name)");
if (name.empty())
return false;
if (!gPageManager)
{
MSG_ERROR("Page manager was not initialized!");
TError::setError();
return false;
}
if (type == BARGRAPH && !gPageManager->getSystemDraw()->existSlider(name))
{
MSG_ERROR("The slider " << name << " doesn't exist!");
return false;
}
else if (type == JOYSTICK && !gPageManager->getSystemDraw()->existCursor(name))
{
MSG_ERROR("The cursor " << name << " doesn't exist!");
return false;
}
if ((type == BARGRAPH && name == sd) || (type == JOYSTICK && name == cd))
return true;
mChanged = true;
if (type == BARGRAPH)
sd = name;
else
cd = name;
if (visible)
refresh();
return true;
}
bool TButton::setFontFileName(const string& name, int /*size*/, int instance)
{
DECL_TRACER("TButton::setFontFileName(const string& name, int size)");
if (name.empty() || !mFonts)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if ((size_t)instance >= sr.size())
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
int id = mFonts->getFontIDfromFile(name);
if (id == -1)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].fi != id)
mChanged = true;
sr[i].fi = id;
}
}
else if (sr[instance].fi != id)
{
mChanged = true;
sr[instance].fi = id;
}
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
bool TButton::setFontName(const string &name, int instance)
{
DECL_TRACER("TButton::setFontName(const string &name, int instance)");
if (name.empty() || !mFonts)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if ((size_t)instance >= sr.size())
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
int id = mFonts->getFontIDfromName(name);
if (id == -1)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].fi != id)
mChanged = true;
sr[i].fi = id;
}
}
else if (sr[instance].fi != id)
{
mChanged = true;
sr[instance].fi = id;
}
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
bool TButton::setBitmap(const string& file, int instance, int index, int justify, int x, int y)
{
DECL_TRACER("TButton::setBitmap(const string& file, int instance, int index, int justify, int x, int y)");
if (instance >= (int)sr.size())
{
MSG_ERROR("Invalid parameters!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
if (!TTPInit::isTP5())
{
for (int i = 0; i < loop; ++i)
{
if (!TTPInit::isTP5())
{
if (sr[inst].bm == file)
{
inst++;
continue;
}
}
mChanged = true;
sr[inst].bm = file;
if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_BITMAP))
{
sk_sp<SkData> image;
SkBitmap bm;
image = readImage(file);
if (image)
{
DecodeDataToBitmap(image, &bm);
if (!bm.empty())
{
TImgCache::addImage(file, bm, _BMTYPE_BITMAP);
sr[inst].bm_width = bm.info().width();
sr[inst].bm_height = bm.info().height();
}
}
}
inst++;
}
}
else // TP5
{
ORIENTATION just = ORI_CENTER_MIDDLE;
if (justify < 0 || justify > 11)
just = ORI_CENTER_MIDDLE;
else
just = static_cast<ORIENTATION>(justify);
// Index 0 = Chameleon image
if (index == 0)
{
SkBitmap bm;
if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_CHAMELEON))
{
sk_sp<SkData> image;
image = readImage(file);
if (image)
{
DecodeDataToBitmap(image, &bm);
if (!bm.empty())
TImgCache::addImage(file, bm, _BMTYPE_BITMAP);
}
}
if (instance < 0) // Set to all instances?
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].mi != file)
{
sr[i].mi = file;
if (!bm.empty())
{
sr[i].mi_width = bm.info().width();
sr[i].mi_height = bm.info().height();
}
mChanged = true;
}
}
}
else
{
if (sr[inst].mi != file)
{
sr[inst].mi = file;
if (!bm.empty())
{
sr[inst].mi_width = bm.info().width();
sr[inst].mi_height = bm.info().height();
}
mChanged = true;
}
}
}
else if (instance > 0)
{
if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_BITMAP))
{
sk_sp<SkData> image;
SkBitmap bm;
image = readImage(file);
if (image)
{
DecodeDataToBitmap(image, &bm);
if (!bm.empty())
{
TImgCache::addImage(file, bm, _BMTYPE_BITMAP);
sr[inst].bm_width = bm.info().width();
sr[inst].bm_height = bm.info().height();
}
}
}
for (size_t i = 0; i < 5; ++i)
{
if (i >= sr[inst].bitmaps.size() && !file.empty())
{
BITMAPS_t bm;
if (i == (static_cast<size_t>(index - 1)))
{
bm.fileName = file;
bm.justification = just;
bm.offsetX = x;
bm.offsetY = y;
mChanged = true;
}
sr[inst].bitmaps.push_back(bm);
}
else if (i == static_cast<size_t>(index - 1))
{
BITMAPS_t bm = sr[inst].bitmaps[i];
if (bm.fileName != file)
{
bm.fileName = file;
bm.justification = just;
bm.offsetX = x;
bm.offsetY = y;
sr[inst].bitmaps[i] = bm;
mChanged = true;
}
}
}
}
}
if (!createButtons(true)) // We're forcing the image to load
return false;
return makeElement(instance);
}
bool TButton::setCameleon(const string& file, int instance)
{
DECL_TRACER("TButton::setCameleon(const string& file, int instance)");
if (file.empty() || instance >= (int)sr.size())
{
MSG_ERROR("Invalid parameters!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].mi == file)
{
inst++;
continue;
}
mChanged = true;
sr[inst].mi = file;
if (!file.empty() && !TImgCache::existBitmap(file, _BMTYPE_CHAMELEON))
{
sk_sp<SkData> image;
SkBitmap bm;
image = readImage(file);
if (image)
{
DecodeDataToBitmap(image, &bm);
if (!bm.empty())
{
TImgCache::addImage(sr[inst].mi, bm, _BMTYPE_CHAMELEON);
sr[inst].mi_width = bm.info().width();
sr[inst].mi_height = bm.info().height();
}
}
}
inst++;
}
if (!createButtons(true)) // We're forcing the image to load
return false;
return makeElement(instance);
}
bool TButton::setInputMask(const std::string& mask)
{
DECL_TRACER("TButton::setInputMask(const std::string& mask)");
vector<char> mTable = { '0', '9', '#', 'L', '?', 'A', 'a', '&', 'C',
'[', ']', '|', '{', '}', '<', '>', '^' };
vector<char>::iterator iter;
for (size_t i = 0; i < mask.length(); ++i)
{
bool found = false;
for (iter = mTable.begin(); iter != mTable.end(); ++iter)
{
if (mask[i] == *iter)
{
found = true;
break;
}
}
if (!found)
{
MSG_WARNING("The mask letter " << mask[i] << " is invalid!");
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
im = mask;
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
void TButton::setActiveInstance(int inst)
{
DECL_TRACER("TButton::setActiveInstance()");
if (inst < 0 || (size_t)inst >= sr.size())
return;
if (mActInstance != inst)
mChanged = true;
mActInstance = inst;
}
SUBVIEW_POSITION_t TButton::getSubViewAnchor()
{
DECL_TRACER("TButton::getSubViewAnchor()");
if (we.empty())
return SVP_CENTER;
else if (strCaseCompare(we, "l/t") == 0)
return SVP_LEFT_TOP;
else if (strCaseCompare(we, "r/b") == 0)
return SVP_RIGHT_BOTTOM;
return SVP_CENTER;
}
bool TButton::getDynamic(int inst)
{
DECL_TRACER("TButton::getDynamic(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return false;
}
return sr[inst].dynamic;
}
void TButton::setDynamic(int d, int inst)
{
DECL_TRACER("TButton::setDynamic(int d, int inst)");
if (inst >= (int)sr.size())
{
MSG_ERROR("Instance is out of size!");
return;
}
bool dyn = (d != 0) ? true : false;
if (inst < 0)
{
vector<SR_T>::iterator iter;
int instance = 0;
for (iter = sr.begin(); iter != sr.end(); ++iter)
{
bool old = iter->dynamic;
iter->dynamic = dyn;
if (old && old != dyn && mActInstance == instance)
{
THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
if (thref)
{
TImageRefresh *mImageRefresh = thref->mImageRefresh;
if (mImageRefresh)
mImageRefresh->stop();
}
mChanged = true;
makeElement(instance);
}
instance++;
}
}
else
{
bool old = sr[inst].dynamic;
sr[inst].dynamic = dyn;
if (old && old != dyn && mActInstance == inst)
{
THR_REFRESH_t *thref = _findResource(mHandle, getParent(), bi);
if (thref)
{
TImageRefresh *mImageRefresh = thref->mImageRefresh;
if (mImageRefresh)
mImageRefresh->stop();
}
mChanged = true;
makeElement(inst);
}
}
}
int TButton::getOpacity(int inst)
{
DECL_TRACER("TButoon::getOpacity(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return 0;
}
return sr[inst].oo;
}
bool TButton::setOpacity(int op, int instance)
{
DECL_TRACER("TButton::setOpacity(int op, int instance)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (op < 0 || op > 255)
{
MSG_ERROR("Invalid opacity " << op << "!");
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].oo == op)
continue;
sr[i].oo = op;
mChanged = true;
}
}
else if (sr[instance].oo != op)
{
sr[instance].oo = op;
mChanged = true;
}
if (!mChanged)
{
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
return true;
}
return makeElement(instance);
}
bool TButton::setFont(int id, int instance)
{
DECL_TRACER("TButton::setFont(int id)");
if (!setFontOnly(id, instance))
return false;
return makeElement(instance);
}
bool TButton::setFontOnly(int id, int instance)
{
DECL_TRACER("TButton::setFontOnly(int id)");
if (instance >= 0 && (size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
{
if (sr[i].fi != id)
{
mChanged = true;
sr[i].fi = id;
}
}
}
else if (sr[instance].fi != id)
{
mChanged = true;
sr[instance].fi = id;
}
return true;
}
void TButton::setLeft(int left)
{
DECL_TRACER("TButton::setLeft(int left)");
if (left < 0)
return;
if (mPosLeft != left)
mChanged = true;
mPosLeft = left;
makeElement(mActInstance);
}
void TButton::setTop(int top)
{
DECL_TRACER("TButton::setTop(int top)");
if (top < 0)
return;
if (mPosTop != top)
mChanged = true;
mPosTop = top;
makeElement(mActInstance);
}
void TButton::setLeftTop(int left, int top)
{
DECL_TRACER("TButton::setLeftTop(int left, int top)");
if (top < 0 || left < 0)
return;
if (mPosLeft != left || mPosTop != top)
mChanged = true;
else
return;
mPosLeft = left;
mPosTop = top;
makeElement(mActInstance);
}
void TButton::setRectangle(int left, int top, int right, int bottom)
{
DECL_TRACER("setRectangle(int left, int top, int right, int bottom)");
if (!gPageManager)
return;
int screenWidth = gPageManager->getSettings()->getWidth();
int screenHeight = gPageManager->getSettings()->getHeight();
int width = right - left;
int height = bottom - top;
if (left >= 0 && right > left && (left + width) < screenWidth)
mPosLeft = left;
if (top >= 0 && bottom > top && (top + height) < screenHeight)
mPosTop = top;
if (left >= 0 && right > left)
wt = width;
if (top >= 0 && bottom > top)
ht = height;
}
void TButton::getRectangle(int *left, int *top, int *height, int *width)
{
DECL_TRACER("TButton::getRectangle(int *left, int *top, int *height, int *width)");
if (left)
*left = mPosLeft;
if (top)
*top = mPosTop;
if (height)
*height = ht;
if (width)
*width = wt;
}
void TButton::resetButton()
{
DECL_TRACER("TButton::resetButton()");
if (mPosLeft == lt && mPosTop == tp && wt == mWidthOrig && ht == mHeightOrig)
return;
mChanged = true;
mPosLeft = lt;
mPosTop = tp;
wt = mWidthOrig;
ht = mHeightOrig;
}
void TButton::setResourceName(const string& name, int instance)
{
DECL_TRACER("TButton::setResourceName(const string& name, int instance)");
if (instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (!sr[inst].dynamic)
{
inst++;
continue;
}
if (sr[inst].bm != name)
mChanged = true;
sr[inst].bm = name;
inst++;
}
}
int TButton::getBitmapJustification(int* x, int* y, int instance)
{
DECL_TRACER("TButton::getBitmapJustification(int* x, int* y, int instance)");
if (instance < 0 || instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << (instance + 1));
return -1;
}
if (x)
*x = sr[instance].jb == 0 ? sr[instance].bx : 0;
if (y)
*y = sr[instance].jb == 0 ? sr[instance].by : 0;
return sr[instance].jb;
}
void TButton::setBitmapJustification(int j, int x, int y, int instance)
{
DECL_TRACER("TButton::setBitmapJustification(int j, int instance)");
if (j < 0 || j > 9 || instance >= (int)sr.size())
{
#if TESTMODE == 1
setScreenDone();
#endif
return;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].jb != j)
mChanged = true;
sr[i].jb = j;
if (j == 0)
{
sr[i].bx = x;
sr[i].by = y;
}
}
}
else
{
if (sr[instance].jb != j)
mChanged = true;
sr[instance].jb = j;
if (j == 0)
{
sr[instance].bx = x;
sr[instance].by = y;
}
}
makeElement();
}
int TButton::getIconJustification(int* x, int* y, int instance)
{
DECL_TRACER("TButton::getIconJustification(int* x, int* y, int instance)");
if (instance < 0 || instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << (instance + 1));
return -1;
}
if (x)
*x = sr[instance].ji == 0 ? sr[instance].ix : 0;
if (y)
*y = sr[instance].ji == 0 ? sr[instance].iy : 0;
return sr[instance].ji;
}
void TButton::setIconJustification(int j, int x, int y, int instance)
{
DECL_TRACER("TButton::setIconJustification(int j, int x, int y, int instance)");
if (j < 0 || j > 9 || instance >= (int)sr.size())
{
#if TESTMODE == 1
setScreenDone();
#endif
return;
}
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].ji != j)
mChanged = true;
sr[i].ji = j;
if (j == 0)
{
sr[i].ix = x;
sr[i].iy = y;
}
}
}
else
{
if (sr[instance].ji != j)
mChanged = true;
sr[instance].ji = j;
if (j == 0)
{
sr[instance].ix = x;
sr[instance].iy = y;
}
}
makeElement();
}
int TButton::getTextJustification(int* x, int* y, int instance)
{
DECL_TRACER("TButton::getTextJustification(int* x, int* y, int instance)");
if (instance < 0 || instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << (instance + 1));
return -1;
}
if (x)
*x = sr[instance].jt == 0 ? sr[instance].tx : 0;
if (y)
*y = sr[instance].jt == 0 ? sr[instance].ty : 0;
return sr[instance].jt;
}
void TButton::setTextJustification(int j, int x, int y, int instance)
{
DECL_TRACER("TButton::setTextJustification(int j, int x, int y, int instance)");
if (!setTextJustificationOnly(j, x, y, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return;
}
makeElement();
}
bool TButton::setTextJustificationOnly(int j, int x, int y, int instance)
{
DECL_TRACER("TButton::setTextJustificationOnly(int j, int x, int y, int instance)");
if (j < 0 || j > 9 || instance >= (int)sr.size())
return false;
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].jt != j)
mChanged = true;
sr[i].jt = (ORIENTATION)j;
if (j == 0)
{
sr[i].tx = x;
sr[i].ty = y;
}
}
}
else
{
if (sr[instance].jt != j)
mChanged = true;
sr[instance].jt = (ORIENTATION)j;
if (j == 0)
{
sr[instance].tx = x;
sr[instance].ty = y;
}
}
return true;
}
string TButton::getText(int inst)
{
DECL_TRACER("TButton::getText(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].te;
}
string TButton::getTextColor(int inst)
{
DECL_TRACER("TButton::getTextColor(int const)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].ct;
}
string TButton::getTextEffectColor(int inst)
{
DECL_TRACER ("TButton::getTextEffectColor(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].ec;
}
void TButton::setTextEffectColor(const string& ec, int instance)
{
DECL_TRACER("TButton::setTextEffectColor(const string& ec, int inst)");
if (!setTextEffectColorOnly(ec, instance))
return;
if (visible)
makeElement();
}
bool TButton::setTextEffectColorOnly(const string& ec, int instance)
{
DECL_TRACER("TButton::setTextEffectColorOnly(const string& ec, int inst)");
if ((size_t)instance >= sr.size())
{
MSG_ERROR("Instance " << instance << " does not exist!");
return false;
}
if (!TColor::isValidAMXcolor(ec))
{
MSG_PROTOCOL("Invalid color >" << ec << "< ignored!");
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].ec.compare(ec) == 0)
{
inst++;
continue;
}
sr[inst].ec = ec;
mChanged = true;
inst++;
}
return true;
}
int TButton::getTextEffect(int inst)
{
DECL_TRACER("TButton::getTextEffect(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return 0;
}
return sr[inst].et;
}
void TButton::setTextEffect(int et, int inst)
{
DECL_TRACER("TButton::setTextEffect(bool et, int inst)");
if (inst >= (int)sr.size())
{
MSG_ERROR("instance " << inst << " is out of bounds!");
return;
}
if (inst < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].et != et)
mChanged = true;
sr[i].et = et;
}
}
else
{
if (sr[inst].et != et)
mChanged = true;
sr[inst].et = et;
}
makeElement();
}
string TButton::getTextEffectName(int inst)
{
DECL_TRACER("TButton::getTextEffectName(int inst)");
if (inst < 0 || inst >= (int)sr.size())
return string();
int idx = 0;
while (sysTefs[idx].idx)
{
if (sysTefs[idx].idx == sr[inst].et)
return sysTefs[idx].name;
idx++;
}
return string();
}
void TButton::setTextEffectName(const string& name, int inst)
{
DECL_TRACER("TButton::setTextEffectName(const string& name, int inst)");
if (inst >= (int)sr.size())
return;
int idx = 0;
while (sysTefs[idx].idx)
{
if (strCaseCompare(sysTefs[idx].name, name) == 0)
{
if (inst < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].et != sysTefs[idx].idx)
mChanged = true;
sr[i].et = sysTefs[idx].idx;
}
}
else
{
if (sr[inst].et != sysTefs[idx].idx)
mChanged = true;
sr[inst].et = sysTefs[idx].idx;
}
makeElement();
break;
}
idx++;
}
}
string TButton::getBitmapName(int inst)
{
DECL_TRACER("TButton::getBitmapName(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].bm;
}
string TButton::getFillColor(int inst)
{
DECL_TRACER("TButton::getFillColor(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].cf;
}
bool TButton::setTextWordWrap(bool state, int instance)
{
DECL_TRACER("TButton::setWorWrap(bool state, int instance)");
if (instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
int stt = state ? 1 : 0;
if (instance < 0)
{
for (size_t i = 0; i < sr.size(); i++)
{
if (sr[i].ww != stt)
mChanged = true;
sr[i].ww = stt;
}
}
else
{
if (sr[instance].ww != stt)
mChanged = true;
sr[instance].ww = stt;
}
return makeElement(instance);
}
void TButton::setMarqueeSpeed(int speed, int inst)
{
DECL_TRACER("TButton::setMarqueeSpeed(int speed, int inst)");
if (inst >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << inst);
return;
}
if (speed < 1 || speed > 10)
{
MSG_ERROR("Speed for marquee line is out of range!");
return;
}
if (inst < 0)
{
for (size_t i = 0; i < sr.size(); ++i)
sr[i].ms = speed;
}
else
sr[inst].ms = speed;
}
int TButton::getMarqueeSpeed(int inst)
{
DECL_TRACER("TButton::getMarqueeSpeed(int inst)");
if (inst >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << inst);
return 1;
}
if (inst <= 0)
return sr[0].ms;
else
return sr[inst].ms;
}
bool TButton::getTextWordWrap(int inst)
{
DECL_TRACER("TButton::getTextWordWrap(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return false;
}
return (sr[inst].ww == 1);
}
int TButton::getFontIndex(int inst)
{
DECL_TRACER("TButton::getFontIndex(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return 0;
}
return sr[inst].fi;
}
bool TButton::setFontIndex(int fi, int instance)
{
DECL_TRACER("TButton::setFontIndex(int fi, int inst)");
if (instance >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
int inst = instance;
int loop = 1;
if (inst < 0)
{
loop = (int)sr.size();
inst = 0;
}
for (int i = 0; i < loop; ++i)
{
if (sr[inst].fi != fi)
mChanged = true;
sr[inst].fi = fi;
inst++;
}
return makeElement(inst);
}
int TButton::getIconIndex(int inst)
{
DECL_TRACER("TButton::getIconIndex(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return 0;
}
return sr[inst].ii;
}
string TButton::getSound(int inst)
{
DECL_TRACER("TButton::getSound(int inst)");
if (inst < 0 || inst >= (int)sr.size())
{
MSG_ERROR("Instance " << inst << " does not exist!");
return string();
}
return sr[inst].sd;
}
void TButton::setSound(const string& sound, int inst)
{
DECL_TRACER("TButton::setSound(const string& sound, int inst)");
if (inst >= (int)sr.size())
{
MSG_ERROR("Invalid instance " << inst);
return;
}
if (inst < 0)
{
for (size_t i = 0; i < sr.size(); i++)
sr[i].sd = sound;
}
else
sr[inst].sd = sound;
#if TESTMODE == 1
__success = true;
setScreenDone();
#endif
}
bool TButton::startAnimation(int st, int end, int time)
{
DECL_TRACER("TButton::startAnimation(int start, int end, int time)");
if (st > end || st < 0 || (size_t)end > sr.size() || time < 0)
{
MSG_ERROR("Invalid parameter: start=" << st << ", end=" << end << ", time=" << time);
return false;
}
if (time <= 1)
{
int inst = end - 1;
if (inst >= 0 && (size_t)inst < sr.size())
{
if (mActInstance != inst)
{
mActInstance = inst;
mChanged = true;
drawButton(inst);
}
}
return true;
}
int start = std::max(1, st);
if (mAniRunning || mThrAni.joinable())
{
MSG_PROTOCOL("Animation is already running!");
return true;
}
int number = end - start;
ulong stepTime = ((ulong)time * 10L) / (ulong)number;
mAniRunTime = (ulong)time * 10L;
try
{
mAniStop = false;
mThrAni = thread([=] { runAnimationRange(start, end, stepTime); });
mThrAni.detach();
}
catch (exception& e)
{
MSG_ERROR("Error starting the button animation thread: " << e.what());
return false;
}
return true;
}
void TButton::_TimerCallback(ulong)
{
mLastBlink.second++;
int months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if ((mLastBlink.year % 4) == 0)
months[1] = 29;
if (mLastBlink.second > 59)
{
mLastBlink.minute++;
mLastBlink.second = 0;
if (mLastBlink.minute > 59)
{
mLastBlink.hour++;
mLastBlink.minute = 0;
if (mLastBlink.hour >= 24)
{
mLastBlink.hour = 0;
mLastBlink.weekday++;
mLastBlink.day++;
if (mLastBlink.weekday > 7)
mLastBlink.weekday = 0;
if (mLastBlink.day > months[mLastBlink.month-1])
{
mLastBlink.day = 1;
mLastBlink.month++;
if (mLastBlink.month > 12)
{
mLastBlink.year++;
mLastBlink.month = 1;
}
}
}
}
}
funcTimer(mLastBlink);
}
void TButton::_imageRefresh(const string& url)
{
DECL_TRACER("TButton::_imageRefresh(const string& url)");
if (prg_stopped || killed || !visible)
return;
if (!gPrjResources)
{
MSG_WARNING("No resources available!");
return;
}
ulong parent = mHandle & 0xffff0000;
getDrawOrder(sr[mActInstance]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
{
TError::clear();
return;
}
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
return;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(&imgButton, mActInstance))
return;
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
RESOURCE_T resource = gPrjResources->findResource(sr[mActInstance].bm);
if (resource.protocol.empty())
{
MSG_ERROR("Resource " << sr[mActInstance].bm << " not found!");
return;
}
THTTPClient *WEBClient = nullptr;
try
{
char *content = nullptr;
size_t length = 0, contentlen = 0;
WEBClient = new THTTPClient;
if (WEBClient && (content = WEBClient->tcall(&length, url, resource.user, resource.password)) == nullptr)
{
if (WEBClient)
delete WEBClient;
return;
}
contentlen = WEBClient->getContentSize();
if (content == nullptr)
{
MSG_ERROR("Server returned no or invalid content!");
delete WEBClient;
return;
}
sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
if (!data)
{
MSG_ERROR("Could not make an image!");
delete WEBClient;
return;
}
SkBitmap image;
if (!DecodeDataToBitmap(data, &image))
{
MSG_ERROR("Error creating an image!");
delete WEBClient;
return;
}
loadImage(&imgButton, image, mActInstance);
delete WEBClient;
}
catch (std::exception& e)
{
if (WEBClient)
delete WEBClient;
MSG_ERROR(e.what());
return;
}
catch(...)
{
if (WEBClient)
delete WEBClient;
MSG_ERROR("Unexpected exception occured. [TButton::_imageRefresh()]");
return;
}
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, mActInstance))
return;
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
// If this is a marquee line, don't draw the text. This will be done
// by the surface.
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
continue;
if (!buttonText(&imgButton, mActInstance))
return;
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, mActInstance))
return;
}
}
if (mGlobalOO >= 0 || sr[mActInstance].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
return;
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[mActInstance].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[mActInstance].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[mActInstance].oo >= 0)
{
oo = (SkScalar)sr[mActInstance].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
//(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped && visible && _displayButton)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = static_cast<int>(static_cast<double>(wt) * gPageManager->getScaleFactor());
rheight = static_cast<int>(static_cast<double>(ht) * gPageManager->getScaleFactor());
rleft = static_cast<int>(static_cast<double>(mPosLeft) * gPageManager->getScaleFactor());
rtop = static_cast<int>(static_cast<double>(mPosTop) * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
rowBytes = imgButton.info().minRowBytes();
mLastImage = imgButton;
}
#endif
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
{
if (gPageManager && gPageManager->getSetMarqueeText())
gPageManager->getSetMarqueeText()(this);
}
}
}
void TButton::registerSystemButton()
{
DECL_TRACER("TButton::registerSystemButton()");
if (mSystemReg)
return;
// If this is a special system button, register it to receive the state
if (ap == 0 && ad == SYSTEM_ITEM_CONNSTATE) // Connection status?
{
MSG_TRACE("Try to register button " << na << " as connection status ...");
if (gAmxNet)
{
gAmxNet->registerNetworkState(bind(&TButton::funcNetwork, this, std::placeholders::_1), mHandle);
mSystemReg = true;
MSG_TRACE("Button registered");
}
else
MSG_WARNING("Network class not initialized!");
}
else if (ap == 0 && ((ad >= SYSTEM_ITEM_STANDARDTIME && ad <= SYSTEM_ITEM_TIME24) || (ad >= SYSTEM_ITEM_DATEWEEKDAY && ad <= SYSTEM_ITEM_DATEYYYYMMDD))) // time or date
{
MSG_TRACE("Try to register button " << na << " as time/date ...");
if (gAmxNet)
{
gAmxNet->registerTimer(bind(&TButton::funcTimer, this, std::placeholders::_1), mHandle);
mSystemReg = true;
MSG_TRACE("Button registered");
}
else
MSG_WARNING("Network class not initialized!");
// if (ad >= SYSTEM_ITEM_STANDARDTIME && ad <= SYSTEM_ITEM_TIME24 && !mTimer)
if (!mTimer)
{
mTimer = new TTimer;
mTimer->setInterval(std::chrono::milliseconds(1000)); // 1 second
mTimer->registerCallback(bind(&TButton::_TimerCallback, this, std::placeholders::_1));
mTimer->run();
}
}
else if (ap == 0 && (ad == SYSTEM_ITEM_BATTERYLEVEL || ad == SYSTEM_ITEM_BATTERYCHARGING)) // Battery status
{
if (gPageManager)
{
#ifdef Q_OS_ANDROID
gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), mHandle);
#endif
#ifdef Q_OS_IOS
gPageManager->regCallbackBatteryState(bind(&TButton::funcBattery, this, std::placeholders::_1, std::placeholders::_2), mHandle);
#endif
}
mSystemReg = true;
}
else if (lp == 0 && lv == SYSTEM_ITEM_CONNSTRENGTH) // Network connection strength
{
if (gPageManager)
gPageManager->regCallbackNetState(bind(&TButton::funcNetworkState, this, std::placeholders::_1), mHandle);
mSystemReg = true;
}
else if (lp == 0 && lv == SYSTEM_ITEM_SYSVOLUME) // System volume
{
int lastLevel = TConfig::getSystemVolume();
if (gPageManager)
{
TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
if (buttonStates)
buttonStates->setLastLevel(lastLevel);
}
mChanged = true;
mSystemReg = true;
}
else if (cp == 0 && type == GENERAL && ch > 0 && isSystemCheckBox(ch))
{
int inst = getButtonInstance(0, ch);
if (inst >= 0)
{
mActInstance = inst;
mChanged = true;
mSystemReg = true;
}
}
else if (ap == 0 && ad > 0 && isSystemTextLine(ad))
{
sr[0].te = sr[1].te = fillButtonText(ad, 0);
mChanged = true;
mSystemReg = true;
}
}
void TButton::addPushFunction(string& func, string& page)
{
DECL_TRACER("TButton::addPushFunction(string& func, string& page)");
vector<string> allFunc = { "Stan", "Prev", "Show", "Hide", "Togg", "ClearG", "ClearP", "ClearA" };
vector<string>::iterator iter;
for (iter = allFunc.begin(); iter != allFunc.end(); ++iter)
{
if (strCaseCompare(*iter, func) == 0)
{
bool found = false;
vector<PUSH_FUNC_T>::iterator iterPf;
if (pushFunc.size() > 0)
{
for (iterPf = pushFunc.begin(); iterPf != pushFunc.end(); ++iterPf)
{
if (strCaseCompare(iterPf->pfType, func) == 0)
{
iterPf->pfName = page;
found = true;
break;
}
}
}
if (!found)
{
PUSH_FUNC_T pf;
pf.pfType = func;
pf.pfName = page;
pushFunc.push_back(pf);
}
break;
}
}
}
void TButton::clearPushFunction(const string& action)
{
DECL_TRACER("TButton::clearPushFunction(const string& action)");
if (pushFunc.empty())
return;
vector<PUSH_FUNC_T>::iterator iter;
for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
{
if (strCaseCompare(iter->pfName, action) == 0)
{
pushFunc.erase(iter);
return;
}
}
}
void TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)
{
DECL_TRACER("TButton::getDrawOrder(const std::string& sdo, DRAW_ORDER *order)");
if (!order)
return;
if (sdo.empty() || sdo.length() != 10)
{
*order = ORD_ELEM_FILL;
*(order+1) = ORD_ELEM_BITMAP;
*(order+2) = ORD_ELEM_BORDER;
*(order+3) = ORD_ELEM_ICON;
*(order+4) = ORD_ELEM_TEXT;
return;
}
int elems = (int)(sdo.length() / 2);
for (int i = 0; i < elems; i++)
{
int e = atoi(sdo.substr(i * 2, 2).c_str());
if (e < 1 || e > 5)
{
MSG_ERROR("Invalid draw order \"" << sdo << "\"!");
TError::setError();
return;
}
*(order+i) = (DRAW_ORDER)e;
}
}
bool TButton::buttonFill(SkBitmap* bm, int instance)
{
DECL_TRACER("TButton::buttonFill(SkBitmap* bm, int instance)");
if (!bm)
{
MSG_ERROR("Invalid bitmap!");
return false;
}
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance << " (range: " << rl << " - " << rh << " [" << sr.size() << "])");
return false;
}
SkColor color = TColor::getSkiaColor(sr[instance].cf);
MSG_DEBUG("Fill color[" << instance << "]: " << sr[instance].cf << " (#" << std::setw(8) << std::setfill('0') << std::hex << color << ")" << std::dec << std::setfill(' ') << std::setw(1));
// We create a new bitmap and fill it with the given fill color. Then
// we put this image over the existing image "bm". In case this method is
// not the first in the draw order, it prevents the button from completely
// overwrite.
SkImageInfo info = bm->info();
SkBitmap bitmap;
if (!allocPixels(info.width(), info.height(), &bitmap))
{
MSG_ERROR("Error allocating a bitmap with size " << info.width() << " x " << info.height() << "!");
return false;
}
bitmap.eraseColor(color); // Fill the new bitmap with the fill color
SkCanvas ctx(*bm, SkSurfaceProps()); // Create a canvas
SkPaint paint; // The paint "device"
paint.setBlendMode(SkBlendMode::kSrcOver); // We're overwriting each pixel
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bitmap); // Technically we need an image. So we convert our new bitmap into an image.
ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Now we put the new image over the existing one.
return true;
}
bool TButton::buttonBitmap(SkBitmap* bm, int inst)
{
DECL_TRACER("TButton::buttonBitmap(SkBitmap* bm, int instane)");
if (prg_stopped || !bm)
return false;
int instance = inst;
if (inst < 0)
instance = 0;
else if ((size_t)inst >= sr.size())
instance = (int)(sr.size() - 1);
bool tp5 = TTPInit::isTP5(); // TRUE = TP5
string bmFile;
/*
* Here we test if we have a cameleon image. If there is a mask (sr[].mi)
* and no frame (sr[].bs) then we have a cameleon image. A bitmap is
* optional. If there is one it will be used to draw with the mask.
* Otherwise the mask may be used as an overlay for a bitmap on another
* button below the mask.
*/
if ((!tp5 && !sr[instance].mi.empty() && sr[instance].bs.empty()) || (tp5 && !sr[instance].mi.empty())) // Chameleon image?
{
if (tp5)
{
if (sr[instance].bitmaps.size() > 0)
bmFile = sr[instance].bitmaps[0].fileName;
}
else
bmFile = sr[instance].bm;
MSG_DEBUG("Chameleon image consisting of mask " << sr[instance].mi << " and bitmap " << (bmFile.empty() ? "NONE" : bmFile) << " ...");
SkBitmap bmMi;
SkBitmap bmBm;
if (!TImgCache::getBitmap(sr[instance].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[instance].mi_width, &sr[instance].mi_height))
{
sk_sp<SkData> data = readImage(sr[instance].mi);
bool loaded = false;
if (data)
{
DecodeDataToBitmap(data, &bmMi);
if (!bmMi.empty())
{
TImgCache::addImage(sr[instance].mi, bmMi, _BMTYPE_CHAMELEON);
loaded = true;
sr[instance].mi_width = bmMi.info().width();
sr[instance].mi_height = bmMi.info().height();
}
}
if(!loaded)
{
MSG_ERROR("Missing image " << sr[instance].mi << "!");
TError::setError();
return false;
}
}
MSG_DEBUG("Chameleon image size: " << bmMi.info().width() << " x " << bmMi.info().height());
SkBitmap imgRed(bmMi);
SkBitmap imgMask;
bool haveBothImages = true;
// On TP5:
// If we have a chameleon image the base is in field "mi", as it was it TP4 already,
// and the first image in the list of images is the mask. This means that we must
// first create the chameleon image out of this images the same as we did for TP4.
// The other images, if there any, will be put on top of the chameleon image.
if (!bmFile.empty())
{
if (!TImgCache::getBitmap(bmFile, &bmBm, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
{
sk_sp<SkData> data = readImage(bmFile);
bool loaded = false;
if (data)
{
DecodeDataToBitmap(data, &bmBm);
if (!bmBm.empty())
{
TImgCache::addImage(bmFile, bmBm, _BMTYPE_BITMAP);
loaded = true;
sr[instance].bm_width = bmBm.info().width();
sr[instance].bm_height = bmBm.info().height();
}
}
if (!loaded)
{
MSG_ERROR("Missing image " << bmFile << "!");
TError::setError();
return false;
}
}
/*
if (!buttonBitmap5(&bmBm, instance))
haveBothImages = false;
else
{
sr[instance].bm_width = bm->info().width();
sr[instance].bm_height = bm->info().height();
if (!imgMask.installPixels(bmBm.pixmap()))
{
MSG_ERROR("Error installing pixmap " << sr[instance].bm << " for chameleon image!");
if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
return false;
imgMask.eraseColor(SK_ColorTRANSPARENT);
haveBothImages = false;
}
}
*/
if (!bmBm.empty())
{
if (!imgMask.installPixels(bmBm.pixmap()))
{
MSG_ERROR("Error installing pixmap " << bmFile << " for chameleon image!");
if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
return false;
imgMask.eraseColor(SK_ColorTRANSPARENT);
haveBothImages = false;
}
}
else
{
MSG_WARNING("No or invalid bitmap! Ignoring bitmap for cameleon image.");
if (!allocPixels(imgRed.info().width(), imgRed.info().height(), &imgMask))
return false;
imgMask.eraseColor(SK_ColorTRANSPARENT);
haveBothImages = false;
}
}
else
haveBothImages = false;
MSG_DEBUG("Bitmap image size: " << bmBm.info().width() << " x " << bmBm.info().height());
MSG_DEBUG("Bitmap mask size: " << imgMask.info().width() << " x " << imgMask.info().height());
SkBitmap img = drawImageButton(imgRed, imgMask, sr[instance].mi_width, sr[instance].mi_height, TColor::getSkiaColor(sr[instance].cf), TColor::getSkiaColor(sr[instance].cb));
if (img.empty())
{
MSG_ERROR("Error creating the cameleon image \"" << sr[instance].mi << "\" / \"" << bmFile << "\"!");
TError::setError();
return false;
}
MSG_DEBUG("Have both images: " << (haveBothImages ? "YES" : "NO"));
SkCanvas ctx(img, SkSurfaceProps());
SkImageInfo info = img.info();
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgMask);
ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
POSITION_t position = calcImagePosition(sr[instance].mi_width, sr[instance].mi_height, SC_BITMAP, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
TError::setError();
return false;
}
SkCanvas can(*bm, SkSurfaceProps());
paint.setBlendMode(SkBlendMode::kSrc);
if (sr[instance].sb == 0)
{
if (!haveBothImages)
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
if (!bmFile.empty())
{
imgMask.installPixels(bmBm.pixmap());
paint.setBlendMode(SkBlendMode::kSrcOver);
_image = SkImages::RasterFromBitmap(imgMask);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
}
else
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
// On TP5 we must draw the other images, if there are any, on top of the one we have already.
if (!buttonBitmap5(bm, instance, true))
{
MSG_WARNING("Couldn't draw all bitmaps!");
}
}
}
else // Scale to fit
{
if (!haveBothImages)
{
SkRect rect;
rect.setXYWH(0, 0, imgRed.info().width(), imgRed.info().height());
sk_sp<SkImage> im = SkImages::RasterFromBitmap(img);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
if (!bmFile.empty())
{
imgMask.installPixels(bmBm.pixmap());
rect.setXYWH(position.left, position.top, position.width, position.height);
im = SkImages::RasterFromBitmap(imgMask);
paint.setBlendMode(SkBlendMode::kSrcOver);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
}
else
{
SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(img);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
// On TP5 we must draw the other images, if there are any, on top of the one we have already.
if (!buttonBitmap5(bm, instance, true))
{
MSG_WARNING("Couldn't draw all bitmaps!");
}
}
}
}
else if (!tp5 && !sr[instance].bm.empty())
{
MSG_TRACE("Drawing normal image " << sr[instance].bm << " ...");
SkBitmap image;
if (!TImgCache::getBitmap(sr[instance].bm, &image, _BMTYPE_BITMAP, &sr[instance].bm_width, &sr[instance].bm_height))
{
sk_sp<SkData> data = readImage(sr[instance].bm);
bool loaded = false;
if (data)
{
DecodeDataToBitmap(data, &image);
if (!image.empty())
{
TImgCache::addImage(sr[instance].mi, image, _BMTYPE_BITMAP);
loaded = true;
sr[instance].bm_width = image.info().width();
sr[instance].bm_height = image.info().height();
}
}
if (!loaded)
{
MSG_ERROR("Missing image " << sr[instance].bm << "!");
return true; // We want the button even without an image
}
}
if (image.empty())
{
MSG_ERROR("Error creating the image \"" << sr[instance].bm << "\"!");
TError::setError();
return false;
}
IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi);
TError::setError();
return false;
}
MSG_DEBUG("Putting bitmap on top of image ...");
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
if (sr[instance].sb == 0) // Scale bitmap?
{ // No, keep size
if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0) // Draw the full image
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
else // We need only a subset of the image
{
MSG_DEBUG("Create a subset of an image ...");
// Create a new Info to have the size of the subset.
SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
size_t byteSize = info.computeMinByteSize();
if (byteSize == 0)
{
MSG_ERROR("Unable to calculate size of image!");
TError::setError();
return false;
}
MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
SkBitmap part; // Bitmap receiving the wanted part from the whole image
SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
image.extractSubset(&part, irect); // Extract the part of the image containg the pixels we want
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
}
}
else // Scale to fit
{
SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
}
else if (tp5 && sr[instance].bitmaps.size() > 0) // TP5: Put all images together
{
MSG_TRACE("Draw TP5 image stack.");
vector<BITMAPS_t>::iterator bmIter;
for (bmIter = sr[instance].bitmaps.begin(); bmIter != sr[instance].bitmaps.end(); ++bmIter)
{
SkBitmap image;
if (!TImgCache::getBitmap(bmIter->fileName, &image, _BMTYPE_BITMAP, &bmIter->width, &bmIter->height))
{
sk_sp<SkData> data = readImage(sr[instance].bm);
bool loaded = false;
if (data)
{
DecodeDataToBitmap(data, &image);
if (!image.empty())
{
TImgCache::addImage(bmIter->fileName, image, _BMTYPE_BITMAP);
loaded = true;
bmIter->width = image.info().width();
bmIter->height = image.info().height();
}
}
if (!loaded)
{
MSG_ERROR("Missing image " << bmIter->fileName << "!");
continue; // We want the button even without an image
}
}
if (image.empty())
{
MSG_ERROR("Error creating the image \"" << bmIter->fileName << "\"!");
continue;
}
IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi);
continue;
}
MSG_DEBUG("Putting bitmap on top of image ...");
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
if (sr[instance].sb == 0) // Scale bitmap?
{ // No, keep size
if ((bmIter->justification == ORI_ABSOLUT && bmIter->offsetX >= 0 && bmIter->offsetY >= 0) || bmIter->justification != ORI_ABSOLUT) // Draw the full image
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
else // We need only a subset of the image
{
MSG_DEBUG("Create a subset of an image ...");
// Create a new Info to have the size of the subset.
SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
size_t byteSize = info.computeMinByteSize();
if (byteSize == 0)
{
MSG_ERROR("Unable to calculate size of image!");
continue;
}
MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
SkBitmap part; // Bitmap receiving the wanted part from the whole image
SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
image.extractSubset(&part, irect); // Extract the part of the image containg the pixels we want
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
}
}
else // Scale to fit
{
SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
}
}
else
{
MSG_DEBUG("No bitmap defined.");
}
return true;
}
/**
* @brief G5: Put all images together
* The method takes all defined images, scales them and put one over the other.
* The result could be combinated with an chameleon image, if there is one.
*
* @param bm A pointer to the bitmap where the result shuld be drawn.
* This pointer must not be NULL.
* @param instances The instance where the bitmaps should be taken from.
* @param ignFirst TRUE = the 1st image in list is ignored.
* @return TRUE on success
*/
bool TButton::buttonBitmap5(SkBitmap* bm, int instance, bool ignFirst)
{
DECL_TRACER("TButton::buttonBitmap5(SkBitmap* bm, int instance, bool ignFirst)");
if (!bm)
{
MSG_WARNING("Method parameter was NULL!");
return false;
}
if (sr[instance].bitmaps.empty())
return true;
vector<BITMAPS_t>::iterator iter;
bool first = true;
for (iter = sr[instance].bitmaps.begin(); iter != sr[instance].bitmaps.end(); ++iter)
{
if (ignFirst && first)
{
first = false;
continue;
}
SkBitmap bmBm;
int width, height;
if (!TImgCache::getBitmap(iter->fileName, &bmBm, _BMTYPE_BITMAP, &width, &height))
{
sk_sp<SkData> data = readImage(iter->fileName);
bool loaded = false;
if (data)
{
DecodeDataToBitmap(data, &bmBm);
if (!bmBm.empty())
{
TImgCache::addImage(iter->fileName, bmBm, _BMTYPE_BITMAP);
loaded = true;
}
}
if (!loaded)
{
MSG_ERROR("Missing image " << iter->fileName << "!");
TError::setError();
return false;
}
width = bmBm.info().width();
height = bmBm.info().height();
}
if (!bmBm.empty())
{
// Map bitmap
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
// Scale bitmap
if (iter->justification == ORI_SCALE_FIT || iter->justification == ORI_SCALE_ASPECT)
{
SkBitmap scaled;
if (!allocPixels(wt, ht, &scaled))
{
MSG_ERROR("Error allocating space for a bitmap!");
return false;
}
SkIRect r;
r.setSize(scaled.info().dimensions()); // Set the dimensions
scaled.erase(SK_ColorTRANSPARENT, r); // Initialize all pixels to transparent
SkCanvas canvas(scaled, SkSurfaceProps()); // Create a canvas
SkRect rect;
if (iter->justification == ORI_SCALE_FIT) // Scale to fit
rect = SkRect::MakeXYWH(0, 0, wt, ht);
else // Scale but maintain aspect ratio
{
double dbl = static_cast<double>(width) / static_cast<double>(height);
if (static_cast<int>(static_cast<double>(ht) * dbl) <= wt)
{
int w = static_cast<int>(static_cast<double>(ht) * dbl);
rect = SkRect::MakeXYWH((wt - w) / 2, 0, w, ht);
}
else
{
int h = static_cast<int>(static_cast<double>(wt) * dbl);
rect = SkRect::MakeXYWH(0, (ht - h) / 2, wt, h);
}
}
sk_sp<SkImage> im = SkImages::RasterFromBitmap(bmBm);
canvas.drawImageRect(im, rect, SkSamplingOptions(), &paint);
bmBm = scaled;
width = bmBm.info().width();
height = bmBm.info().height();
}
// Justify bitmap
int x, y;
switch(iter->justification)
{
case ORI_ABSOLUT:
x = iter->offsetX;
y = iter->offsetY;
break;
case ORI_BOTTOM_LEFT:
x = 0;
y = ht - height;
break;
case ORI_BOTTOM_MIDDLE:
x = (wt - width) / 2;
y = ht - height;
break;
case ORI_BOTTOM_RIGHT:
x = wt - width;
y = ht -height;
break;
case ORI_CENTER_LEFT:
x = 0;
y = (ht - height) / 2;
break;
case ORI_CENTER_MIDDLE:
x = (wt - width) / 2;
y = (ht - height) / 2;
break;
case ORI_CENTER_RIGHT:
x = wt - width;
y = (ht - height) / 2;
break;
case ORI_TOP_LEFT:
x = 0;
y = 0;
break;
case ORI_TOP_MIDDLE:
x = (wt - width) / 2;
y = 0;
break;
case ORI_TOP_RIGHT:
x = wt - width;
y = 0;
break;
default:
x = 0;
y = 0;
}
SkRect rect = SkRect::MakeXYWH(x, y, width, height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(bmBm);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
else
{
MSG_WARNING("No or invalid bitmap!");
return false;
}
}
return true;
}
bool TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)
{
DECL_TRACER("TButton::buttonDynamic(SkBitmap* bm, int instance, bool show, bool *state)");
if (prg_stopped)
return false;
if (!gPrjResources)
{
MSG_ERROR("Internal error: Global resource class not initialized!");
return false;
}
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
if (!sr[instance].dynamic)
{
MSG_WARNING("Button " << bi << ": \"" << na << "\" is not for remote image!");
return false;
}
if (!visible)
{
MSG_DEBUG("Dynamic button " << handleToString(mHandle) << " is invisible. Will not draw it.");
return true;
}
MSG_DEBUG("Dynamic button " << handleToString(mHandle) << " will be drawn ...");
size_t idx = 0;
if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
{
MSG_ERROR("There exists no image resource!");
return false;
}
RESOURCE_T resource = gPrjResources->findResource((int)idx, sr[instance].bm);
if (resource.protocol.empty())
{
MSG_WARNING("Resource " << sr[instance].bm << " not found!");
return true;
}
string path = resource.path;
if (!resource.file.empty())
path += "/" + resource.file;
string url = THTTPClient::makeURLs(toLower(resource.protocol), resource.host, 0, path);
if (url.empty())
{
MSG_DEBUG("No URL, no bitmap!");
return true; // We have no image but the button still exists
}
SkBitmap image;
if (TImgCache::getBitmap(url, &image, _BMTYPE_URL))
{
MSG_DEBUG("Found image \"" << url << "\" in the cache. Will reuse it.");
IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : image.info().width()), (sr[instance].sb ? isize.height : image.info().height()), SC_BITMAP, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi);
TError::setError();
return false;
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
if (sr[instance].sb == 0) // Scale bitmap?
{ // No, keep size
if ((sr[instance].jb == 0 && sr[instance].bx >= 0 && sr[instance].by >= 0) || sr[instance].jb != 0) // Draw the full image
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
else // We need only a subset of the image
{
MSG_DEBUG("Create a subset of an image ...");
// Create a new Info to have the size of the subset.
SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
size_t byteSize = info.computeMinByteSize();
if (byteSize == 0)
{
MSG_ERROR("Unable to calculate size of image!");
TError::setError();
return false;
}
MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
SkBitmap part; // Bitmap receiving the wanted part from the whole image
SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
image.extractSubset(&part, irect); // Extract the part of the image containg the pixels we want
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
}
}
else // Scale to fit
{
SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
return true;
}
try
{
// First me must add the credential for the image into a bitmap cache element
BITMAP_CACHE bc;
bc.top = mPosTop;
bc.left = mPosLeft;
bc.width = wt;
bc.height = ht;
bc.bi = bi;
bc.show = show;
bc.handle = getHandle();
bc.parent = getParent();
bc.bitmap = *bm;
addToBitmapCache(bc);
if (state)
*state = true; // Prevent the calling method from displaying the button
MSG_TRACE("Starting thread for loading a dynamic image ...");
mThrRes = std::thread([=] { this->funcResource(&resource, url, bc, instance); });
MSG_TRACE("Thread started. Detaching ...");
mThrRes.detach();
MSG_TRACE("Thread is running and detached.");
}
catch (std::exception& e)
{
MSG_ERROR("Error starting the resource thread: " << e.what());
}
return true;
}
/*
* Draws the elements of a button starting at the point where the bitmap was
* already drawed. Everything coming afterwards acording to the draw order
* is drawed in the desired order.
* This method is called out of a thread to draw a button with an external
* image coming from a WEB server.
*/
bool TButton::drawAlongOrder(SkBitmap *imgButton, int instance)
{
DECL_TRACER("TButton::drawAlongOrder(SkBitmap *imgButton, int instance)");
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
bool cont = false;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (!cont && mDOrder[i] == ORD_ELEM_BITMAP)
{
cont = true;
continue;
}
else if (!cont)
continue;
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(imgButton, instance))
return false;
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(imgButton, instance))
return false;
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
// If this is a marquee line, don't draw the text. This will be done
// by the surface.
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
continue;
if (!buttonText(imgButton, instance))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(imgButton, instance))
return false;
}
}
return true;
}
void TButton::funcResource(const RESOURCE_T* resource, const std::string& url, BITMAP_CACHE bc, int instance)
{
DECL_TRACER("TButton::funcResource(RESOURCE_T* resource, std::string& url, SkBitmap* bm, int instance)");
if (prg_stopped || killed || _restart_ || !resource)
return;
if (resource->refresh > 0 && !resource->dynamo) // Periodically refreshing image?
{
MSG_DEBUG("Retrieving periodicaly refreshed image");
if (!bc.handle || !bc.parent || bc.bi <= 1)
{
MSG_ERROR("Invalid button. Can't make a dynamo image!");
return;
}
THR_REFRESH_t *thref = _findResource(bc.handle, bc.parent, bc.bi);
TImageRefresh *mImageRefresh = nullptr;
if (!thref)
{
MSG_DEBUG("Creating a new refresh thread");
mImageRefresh = new TImageRefresh();
mImageRefresh->registerCallback(bind(&TButton::_imageRefresh, this, std::placeholders::_1));
mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
mImageRefresh->setUsername(resource->user);
mImageRefresh->setPassword(resource->password);
if (resource->preserve)
mImageRefresh->setRunOnce();
_addResource(mImageRefresh, bc.handle, bc.parent, bc.bi);
}
else
{
mImageRefresh = thref->mImageRefresh;
if (!mImageRefresh)
{
MSG_ERROR("Error creating a new refresh class!");
return;
}
mImageRefresh->setInterval(std::chrono::seconds(resource->refresh));
mImageRefresh->setUsername(resource->user);
mImageRefresh->setPassword(resource->password);
if (resource->preserve)
mImageRefresh->setRunOnce();
}
if (mImageRefresh->isRunning())
mImageRefresh->stopWait();
if (!mImageRefresh->isRunning() && !_restart_)
{
MSG_DEBUG("Starting a refresh thread.");
mImageRefresh->run(url);
}
}
else if (resource->refresh == 0 && !resource->dynamo)
{
MSG_DEBUG("Retrieving single image");
if (bc.handle == 0)
{
MSG_ERROR("Invalid bitmap cache!");
return;
}
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return;
}
// Check whether we have this image already
SkBitmap bitm;
bool cached = false;
cached = TImgCache::getBitmap(url, &bitm, _BMTYPE_URL);
BITMAP_CACHE bmCache = getBCentryByHandle(bc.handle, bc.parent);
if (!cached) // If the bitmap was not in cache we must load it
{
MSG_DEBUG("Image not in cache. Downloading it ...");
THTTPClient *WEBClient = nullptr;
if (bmCache.handle == 0)
{
MSG_ERROR("Couldn't find the handle " << handleToString(bc.handle) << " in bitmap cache!");
return;
}
char *content = nullptr;
size_t length = 0, contentlen = 0;
WEBClient = new THTTPClient;
if (!WEBClient || (content = WEBClient->tcall(&length, url, resource->user, resource->password)) == nullptr)
{
if (WEBClient)
delete WEBClient;
if (bc.show)
{
setReady(bmCache.handle);
showBitmapCache();
}
else
setInvalid(bc.handle);
return;
}
contentlen = WEBClient->getContentSize();
MSG_DEBUG("Loaded " << contentlen << " bytes:");
sk_sp<SkData> data = SkData::MakeWithCopy(content, contentlen);
if (!data || _restart_)
{
delete WEBClient;
MSG_ERROR("Error making image data!");
if (bc.show)
{
setReady(bmCache.handle);
showBitmapCache();
}
else
setInvalid(bc.handle);
return;
}
SkBitmap image;
if (!DecodeDataToBitmap(data, &image))
{
delete WEBClient;
MSG_ERROR("Error creating an image!");
if (bc.show)
{
setReady(bmCache.handle);
showBitmapCache();
}
else
setInvalid(bc.handle);
return;
}
// Put this image into the static image cache
TImgCache::addImage(url, image, _BMTYPE_URL);
// Make the button complete
loadImage(&bmCache.bitmap, image, instance);
drawAlongOrder(&bmCache.bitmap, instance);
setBCBitmap(bmCache.handle, bmCache.bitmap);
setReady(bmCache.handle);
delete WEBClient;
// Display the image
showBitmapCache();
return;
}
else
{
MSG_DEBUG("Found image in cache. Using it ...");
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return;
}
loadImage(&bmCache.bitmap, bitm, instance);
setInvalid(bc.handle);
if (bc.show && _displayButton)
{
TBitmap image((unsigned char *)bmCache.bitmap.getPixels(), bmCache.bitmap.info().width(), bmCache.bitmap.info().height());
_displayButton(bc.handle, bc.parent, image, bc.width, bc.height, bc.left, bc.top, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
mChanged = false;
}
}
}
else if (!_restart_)
{
MSG_DEBUG("Retrieving a video");
if (_playVideo && !prg_stopped)
{
ulong parent = (mHandle >> 16) & 0x0000ffff;
_playVideo(mHandle, parent, mPosLeft, mPosTop, wt, ht, url, resource->user, resource->password);
}
}
}
#ifdef Q_OS_ANDROID
void TButton::funcBattery(int level, bool charging, int /* chargeType */)
{
DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
// Battery level is always a bargraph
if (ap == 0 && ad == SYSTEM_ITEM_BATTERYLEVEL) // Not charging
{
mEnabled = !charging;
mChanged = true;
if (!mEnabled && visible)
hide(true);
else if (mEnabled)
{
visible = true;
drawBargraph(mActInstance, level, visible);
}
}
else if (ap == 0 && ad == SYSTEM_ITEM_BATTERYCHARGING) // Charging
{
mEnabled = charging;
mChanged = true;
if (!mEnabled && visible)
hide(true);
else if (mEnabled)
{
visible = true;
drawBargraph(mActInstance, level, visible);
}
}
}
#endif
#ifdef Q_OS_IOS
void TButton::funcBattery(int level, int state)
{
DECL_TRACER("TButton::funcBattery(int level, bool charging, int chargeType)");
// Battery level is always a bargraph
if (ap == 0 && ad == SYSTEM_ITEM_BATTERYLEVEL) // Not charging
{
mEnabled = (state == 1 || state == 3);
mChanged = true;
if (!mEnabled && visible)
hide(true);
else if (mEnabled)
{
visible = true;
drawBargraph(mActInstance, level, visible);
}
}
else if (ap == 0 && ad == SYSTEM_ITEM_BATTERYCHARGING) // Charging
{
mEnabled = (state == 2);
mChanged = true;
if (!mEnabled && visible)
hide(true);
else if (mEnabled)
{
visible = true;
drawBargraph(mActInstance, level, visible);
}
}
}
#endif
void TButton::funcNetworkState(int level)
{
DECL_TRACER("TButton::funcNetworkState(int level)");
if (level >= rl && level <= rh)
{
int lastLevel = level;
if (gPageManager)
{
TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
if (buttonStates)
buttonStates->setLastLevel(level);
else
MSG_ERROR("Button states not found!");
}
mChanged = true;
drawMultistateBargraph(lastLevel);
}
}
bool TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)
{
DECL_TRACER("TButton::loadImage(SkBitmap* bm, SkBitmap& image, int instance)");
if (!bm)
{
MSG_WARNING("Got no image to load!");
return false;
}
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
SkImageInfo info = image.info();
IMAGE_SIZE_t isize = calcImageSize(image.info().width(), image.info().height(), instance, true);
POSITION_t position = calcImagePosition((sr[instance].sb ? isize.width : info.width()), (sr[instance].sb ? isize.height : info.height()), SC_BITMAP, instance);
// POSITION_t position = calcImagePosition(info.width(), info.height(), SC_BITMAP, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi);
return false;
}
MSG_DEBUG("New image position: left=" << position.left << ", top=" << position.top << ", width=" << position.width << ", height=" << position.height);
MSG_DEBUG("Image size : width=" << info.width() << ", height=" << info.height());
MSG_DEBUG("Bitmap size: width=" << bm->info().width() << ", height=" << bm->info().height());
MSG_DEBUG("Putting bitmap on top of image ...");
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
SkCanvas can(*bm, SkSurfaceProps());
if (sr[instance].sb == 0)
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
else // Scale to fit
{
// paint.setFilterQuality(kHigh_SkFilterQuality);
SkRect rect = SkRect::MakeXYWH(position.left, position.top, isize.width, isize.height);
sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
}
return true;
}
bool TButton::barLevel(SkBitmap* bm, int, int level)
{
DECL_TRACER("TButton::barLevel(SkBitmap* bm, int inst, int level)");
if (!sr[0].mi.empty() && sr[0].bs.empty() && !sr[1].bm.empty()) // Chameleon image?
{
MSG_TRACE("Chameleon image ...");
SkBitmap bmMi, bmBm;
TImgCache::getBitmap(sr[0].mi, &bmMi, _BMTYPE_CHAMELEON, &sr[0].mi_width, &sr[0].mi_height);
TImgCache::getBitmap(sr[1].bm, &bmBm, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height);
SkBitmap imgRed(bmMi);
SkBitmap imgMask(bmBm);
SkBitmap img;
SkPixmap pixmapRed = imgRed.pixmap();
SkPixmap pixmapMask;
if (!imgMask.empty())
pixmapMask = imgMask.pixmap();
int width = sr[0].mi_width;
int height = sr[0].mi_height;
int startX = 0;
int startY = 0;
// Calculation: width / <effective pixels> * level
// Calculation: height / <effective pixels> * level
if (dr.compare("horizontal") == 0)
width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
else
{
height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
startY = sr[0].mi_height - height;
height = sr[0].mi_height;
}
if (!allocPixels(sr[0].mi_width, sr[0].mi_height, &img))
return false;
SkCanvas canvas(img);
SkColor col1 = TColor::getSkiaColor(sr[1].cf);
SkColor col2 = TColor::getSkiaColor(sr[1].cb);
for (int ix = 0; ix < sr[0].mi_width; ix++)
{
for (int iy = 0; iy < sr[0].mi_height; iy++)
{
SkPaint paint;
SkColor pixel;
if (ix >= startX && ix < width && iy >= startY && iy < height)
{
SkColor pixelRed = pixmapRed.getColor(ix, iy);
SkColor pixelMask;
if (!imgMask.empty())
pixelMask = pixmapMask.getColor(ix, iy);
else
pixelMask = SK_ColorWHITE;
pixel = baseColor(pixelRed, pixelMask, col1, col2);
}
else
pixel = SK_ColorTRANSPARENT;
paint.setColor(pixel);
canvas.drawPoint(ix, iy, paint);
}
}
if (img.empty())
{
MSG_ERROR("Error creating the cameleon image \"" << sr[0].mi << "\" / \"" << sr[0].bm << "\"!");
TError::setError();
return false;
}
SkCanvas ctx(img, SkSurfaceProps());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcATop);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgMask);
ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
POSITION_t position = calcImagePosition(sr[0].mi_width, sr[0].mi_height, SC_BITMAP, 0);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi << ": " << na);
TError::setError();
return false;
}
SkCanvas can(*bm, SkSurfaceProps());
paint.setBlendMode(SkBlendMode::kSrc);
_image = SkImages::RasterFromBitmap(img);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
else if (!sr[0].bm.empty() && !sr[1].bm.empty())
{
MSG_TRACE("Drawing normal image ...");
SkBitmap image1, image2;
TImgCache::getBitmap(sr[0].bm, &image1, _BMTYPE_BITMAP, &sr[0].bm_width, &sr[0].bm_height); // State when level = 0%
TImgCache::getBitmap(sr[1].bm, &image2, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height); // State when level = 100%
SkCanvas can_bm(*bm, SkSurfaceProps());
if (image1.empty())
{
MSG_ERROR("Error creating the image \"" << sr[0].bm << "\"!");
TError::setError();
return false;
}
if (image2.empty())
{
MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
TError::setError();
return false;
}
int width = sr[1].bm_width;
int height = sr[1].bm_height;
int startX = 0;
int startY = 0;
MSG_DEBUG("Image size: " << width << " x " << height);
// Calculation: width / <effective pixels> * level
// Calculation: height / <effective pixels> * level
if (dr.compare("horizontal") == 0)
width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
else
{
height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
startY = sr[0].bm_height - height;
height = sr[0].bm_height;
}
MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
MSG_TRACE("Creating bargraph ...");
SkBitmap img_bar;
if (!allocPixels(sr[1].bm_width, sr[1].bm_height, &img_bar))
return false;
img_bar.eraseColor(SK_ColorTRANSPARENT);
SkCanvas bar(img_bar, SkSurfaceProps());
for (int ix = 0; ix < sr[1].bm_width; ix++)
{
for (int iy = 0; iy < sr[1].bm_height; iy++)
{
SkPaint paint;
SkColor pixel;
if (ix >= startX && ix < width && iy >= startY && iy < height)
pixel = image2.getColor(ix, iy);
else
pixel = SK_ColorTRANSPARENT;
paint.setColor(pixel);
bar.drawPoint(ix, iy, paint);
}
}
POINT_t point = getImagePosition(sr[0].bm_width, sr[0].bm_height);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image1);
can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint);
paint.setBlendMode(SkBlendMode::kSrcATop);
_image = SkImages::RasterFromBitmap(img_bar);
can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint); // Draw the above created image over the 0% image
}
else if (sr[0].bm.empty() && !sr[1].bm.empty()) // Only one bitmap in the second instance
{
MSG_TRACE("Drawing second image " << sr[1].bm << " ...");
SkBitmap image;
TImgCache::getBitmap(sr[1].bm, &image, _BMTYPE_BITMAP, &sr[1].bm_width, &sr[1].bm_height); // State when level = 100%
SkCanvas can_bm(*bm, SkSurfaceProps());
if (image.empty())
{
MSG_ERROR("Error creating the image \"" << sr[1].bm << "\"!");
TError::setError();
return false;
}
int width = sr[1].bm_width;
int height = sr[1].bm_height;
int startX = 0;
int startY = 0;
// Calculation: width / <effective pixels> * level
// Calculation: height / <effective pixels> * level
if (dr.compare("horizontal") == 0)
width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
else
{
height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
startY = sr[0].bm_height - height;
height = sr[0].bm_height;
}
MSG_DEBUG("dr=" << dr << ", startX=" << startX << ", startY=" << startY << ", width=" << width << ", height=" << height << ", level=" << level);
MSG_TRACE("Creating bargraph ...");
SkBitmap img_bar;
if (!allocPixels(sr[1].bm_width, sr[1].bm_height, &img_bar))
return false;
img_bar.eraseColor(SK_ColorTRANSPARENT);
SkCanvas bar(img_bar, SkSurfaceProps());
SkPaint pt;
for (int ix = 0; ix < sr[1].bm_width; ix++)
{
for (int iy = 0; iy < sr[1].bm_height; iy++)
{
SkColor pixel;
if (ix >= startX && ix < width && iy >= startY && iy < height)
pixel = image.getColor(ix, iy);
else
pixel = SK_ColorTRANSPARENT;
pt.setColor(pixel);
bar.drawPoint(ix, iy, pt);
}
}
POINT_t point = getImagePosition(sr[1].bm_width, sr[1].bm_height);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img_bar);
can_bm.drawImage(_image, point.x, point.y, SkSamplingOptions(), &paint); // Draw the above created image over the 0% image
}
else
{
MSG_TRACE("No bitmap defined.");
int width = wt;
int height = ht;
int startX = 0;
int startY = 0;
// Calculation: width / <effective pixels> * level = <level position>
// Calculation: height / <effective pixels> * level = <level position>
if (dr.compare("horizontal") == 0)
width = static_cast<int>(static_cast<double>(width) / static_cast<double>(rh - rl) * static_cast<double>(level));
else
{
height = static_cast<int>(static_cast<double>(height) / static_cast<double>(rh - rl) * static_cast<double>(level));
startY = ht - height;
height = ht;
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
SkCanvas can(*bm, SkSurfaceProps());
paint.setStyle(SkPaint::kFill_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(4);
paint.setColor(TColor::getSkiaColor(sr[1].cf));
MSG_DEBUG("Drawing rectangle: X=" << startX << ", Y=" << startY << ", W=" << width << ", H=" << height << ", level=" << level);
SkRect dst;
dst.setXYWH(startX, startY, width, height);
can.drawRect(dst, paint);
// If we have a slider button defined, we must draw it. To do it, we
// must look into the system resources to find the credentials to draw
// the button.
if (!sd.empty())
{
MSG_DEBUG("Attempt to draw the slider button \"" << sd << "\".");
int innerW = 0;
int innerH = 0;
SkBitmap slButton = drawSliderButton(sd, TColor::getSkiaColor(sc));
if (slButton.empty())
{
MSG_ERROR("Error drawing the slicer button " << sd);
return true;
}
double scaleW, scaleH;
int border_size = getBorderSize(sr[0].bs);
if (dr.compare("horizontal") != 0)
{
double scale;
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;
innerW = width;
scale = static_cast<double>(wt - border_size * 2) / static_cast<double>(slButton.info().width());
scaleW = scale;
scaleH = 1.0;
innerH = height - innerH;
}
else
{
double scale;
scale = static_cast<double>(ht - border_size * 2) / static_cast<double>(slButton.info().height());
scaleW = 1.0;
scaleH = scale;
innerH = height;
innerW = width;
}
if (scaleImage(&slButton, scaleW, scaleH))
{
int w = slButton.info().width();
int h = slButton.info().height();
if (dr.compare("horizontal") == 0)
{
int pos = innerW;
dst.setXYWH(pos - w / 2, border_size, w, h);
}
else
{
int pos = innerH;
dst.setXYWH(border_size, pos - h / 2, w, h);
}
SkPaint pnt;
pnt.setBlendMode(SkBlendMode::kSrcOver);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(slButton);
can.drawImageRect(_image, dst, SkSamplingOptions(), &pnt);
}
}
}
return true;
}
POINT_t TButton::getImagePosition(int width, int height)
{
DECL_TRACER("TButton::getImagePosition(int width, int height)");
POINT_t point;
switch (sr[0].jb)
{
case ORI_ABSOLUT:
point.x = sr[0].bx;
point.y = ht - sr[0].by;
break;
case ORI_TOP_LEFT:
point.x = 0;
point.y = 0;
break;
case ORI_TOP_MIDDLE:
point.x = (wt - width) / 2;
point.y = 0;
break;
case ORI_TOP_RIGHT:
point.x = wt - width;
point.y = 0;
break;
case ORI_CENTER_LEFT:
point.x = 0;
point.y = (ht - height) / 2;
break;
case ORI_CENTER_MIDDLE:
point.x = (wt - width) / 2;
point.y = (ht - height) / 2;
break;
case ORI_CENTER_RIGHT:
point.x = wt - width;
point.y = (ht - height) / 2;
break;
case ORI_BOTTOM_LEFT:
point.x = 0;
point.y = ht - height;
break;
case ORI_BOTTOM_MIDDLE:
point.x = (wt - width) / 2;
point.y = ht - height;
break;
case ORI_BOTTOM_RIGHT:
point.x = wt - width;
point.y = ht - height;
break;
}
return point;
}
SkBitmap TButton::drawSliderButton(const string& slider, SkColor col)
{
DECL_TRACER("TButton::drawSliderButton(const string& slider)");
SkBitmap slButton;
// First we look for the slider button.
if (!gPageManager || !gPageManager->getSystemDraw()->existSlider(slider))
return slButton;
// There exists one with the wanted name. We grab it and create
// the images from the files.
SLIDER_STYLE_t sst;
if (!gPageManager->getSystemDraw()->getSlider(slider, &sst)) // should never be true!
{
MSG_ERROR("No slider entry found!");
return slButton;
}
int width, height;
if (dr.compare("horizontal") != 0)
{
width = (sst.fixedSize / 2) * 2 + sst.fixedSize;
height = sst.fixedSize;
}
else
{
width = sst.fixedSize;
height = (sst.fixedSize / 2) * 2 + sst.fixedSize;
}
// Retrieve all available slider graphics files from the system
vector<SLIDER_t> sltList = gPageManager->getSystemDraw()->getSliderFiles(slider);
if (sltList.empty())
{
MSG_ERROR("No system slider graphics found!");
return SkBitmap();
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
if (!allocPixels(width, height, &slButton))
return slButton;
slButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas slCan(slButton, SkSurfaceProps());
vector<SLIDER_t>::iterator sltIter;
// Loop through list of slider graphic files
for (sltIter = sltList.begin(); sltIter != sltList.end(); ++sltIter)
{
SkBitmap slPart;
SkBitmap slPartAlpha;
SkRect dst;
if (dr.compare("horizontal") != 0 && (sltIter->type == SGR_LEFT || sltIter->type == SGR_RIGHT || sltIter->type == SGR_VERTICAL)) // vertical slider
{
if (!retrieveImage(sltIter->path, &slPart)) // Get the mask
{
MSG_ERROR("Missing slider button mask image " << sltIter->path);
return SkBitmap();
}
if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha)) // Get the alpha mask
{
MSG_ERROR("Missing slider button alpha image " << sltIter->pathAlpha);
return SkBitmap();
}
SkBitmap sl = combineImages(slPart, slPartAlpha, col);
if (sl.empty())
return sl;
switch (sltIter->type)
{
case SGR_LEFT: dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
case SGR_VERTICAL:
stretchImageWidth(&sl, sst.fixedSize);
dst.setXYWH(sst.fixedSize / 2, 0, sl.info().width(), sl.info().height());
break;
case SGR_RIGHT: dst.setXYWH((sst.fixedSize / 2) + sst.fixedSize, 0, sl.info().width(), sl.info().height()); break;
default:
MSG_WARNING("Invalid type " << sltIter->type << " found!");
}
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(sl);
slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
}
else if (dr.compare("horizontal") == 0 && (sltIter->type == SGR_TOP || sltIter->type == SGR_BOTTOM || sltIter->type == SGR_HORIZONTAL)) // horizontal slider
{
if (!retrieveImage(sltIter->path, &slPart))
{
MSG_ERROR("Missing slider button image " << sltIter->path);
return SkBitmap();
}
if (!retrieveImage(sltIter->pathAlpha, &slPartAlpha))
{
MSG_ERROR("Missing slider button image " << sltIter->pathAlpha);
return SkBitmap();
}
SkBitmap sl = combineImages(slPart, slPartAlpha, col);
if (sl.empty())
return sl;
switch (sltIter->type)
{
case SGR_TOP: dst.setXYWH(0, 0, sl.info().width(), sl.info().height()); break;
case SGR_HORIZONTAL:
stretchImageHeight(&sl, sst.fixedSize);
dst.setXYWH(0, sst.fixedSize / 2, sl.info().width(), sl.info().height());
break;
case SGR_BOTTOM: dst.setXYWH(0, (sst.fixedSize / 2) + sst.fixedSize, sl.info().width(), sl.info().height()); break;
default:
MSG_WARNING("Invalid type " << sltIter->type << " found!");
}
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(sl);
slCan.drawImageRect(_image, dst, SkSamplingOptions(), &paint);
}
}
return slButton;
}
bool TButton::buttonIcon(SkBitmap* bm, int instance)
{
DECL_TRACER("TButton::buttonIcon(SkBitmap* bm, int instance)");
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
if (sr[instance].ii <= 0)
{
MSG_TRACE("No icon defined!");
return true;
}
MSG_DEBUG("Drawing an icon ...");
if (!gIcons)
{
MSG_WARNING("No icons were defined!");
return true;
}
string file = gIcons->getFile(sr[instance].ii);
if (file.empty())
{
MSG_WARNING("The icon " << sr[instance].ii << " was not found in table!");
return true;
}
MSG_DEBUG("Loading icon file " << file);
sk_sp<SkData> image;
SkBitmap icon;
if (!(image = readImage(file)))
return true;
DecodeDataToBitmap(image, &icon);
if (icon.empty())
{
MSG_WARNING("Could not create an icon for element " << sr[instance].ii << " on button " << bi << " (" << na << ")");
return true;
}
SkImageInfo info = icon.info();
POSITION_t position = calcImagePosition(icon.width(), icon.height(), SC_ICON, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the position of the image for button number " << bi);
TError::setError();
return false;
}
MSG_DEBUG("Putting Icon on top of bitmap ...");
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
if (position.overflow)
{
SkRect irect;
SkRect bdst;
SkBitmap dst;
int left = (position.left >= 0) ? 0 : position.left * -1;
int top = (position.top >= 0) ? 0 : position.top * -1;
int width = std::min(wt, info.width());
int height = std::min(ht, info.height());
irect.setXYWH(left, top, width, height);
bm->getBounds(&bdst);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(icon);
can.drawImageRect(_image, irect, bdst, SkSamplingOptions(), &paint, SkCanvas::kStrict_SrcRectConstraint);
}
else
{
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(icon);
can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
}
return true;
}
bool TButton::buttonText(SkBitmap* bm, int inst)
{
DECL_TRACER("TButton::buttonText(SkBitmap* bm, int inst)");
int instance = inst;
if ((size_t)instance >= sr.size())
instance = (int)(sr.size() - 1);
else if (instance < 0)
instance = 0;
if (sr[instance].te.empty()) // Is there a text?
{ // No, then return
MSG_DEBUG("Empty text string.");
return true;
}
if (!mFonts) // Do we have any fonts?
{ // No, warn and return
MSG_WARNING("No fonts available to write a text!");
return true;
}
sk_sp<SkTypeface> typeFace;
FONT_T font;
if (gPageManager && !gPageManager->getSettings()->isTP5())
{
MSG_DEBUG("Searching for font number " << sr[instance].fi << " with text " << sr[instance].te);
font = mFonts->getFont(sr[instance].fi);
if (font.file.empty())
{
MSG_WARNING("No font file name found for font " << sr[instance].fi);
return true;
}
typeFace = mFonts->getTypeFace(sr[instance].fi);
}
else
{
MSG_DEBUG("Searching for font " << sr[instance].ff << " with size " << sr[instance].fs << " and text " << sr[instance].te);
font.file = sr[instance].ff;
font.size = sr[instance].fs;
typeFace = mFonts->getTypeFace(sr[instance].ff);
SkString family;
font.fullName = font.name = sr[instance].ff;
}
SkCanvas canvas(*bm);
if (!typeFace)
{
MSG_WARNING("Error creating type face " << font.fullName);
}
SkScalar fontSizePt = ((SkScalar)font.size * 1.322);
SkFont skFont;
if (typeFace && typeFace->countTables() > 0)
skFont.setTypeface(typeFace);
skFont.setSize(fontSizePt);
skFont.setEdging(SkFont::Edging::kAntiAlias);
MSG_DEBUG("Wanted font size: " << font.size << ", this is " << fontSizePt << " pt");
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(TColor::getSkiaColor(sr[instance].ct));
paint.setStyle(SkPaint::kFill_Style);
SkFontMetrics metrics;
skFont.getMetrics(&metrics);
int lines = numberLines(sr[instance].te);
// MSG_DEBUG("fAvgCharWidth: " << metrics.fAvgCharWidth);
// MSG_DEBUG("fCapHeight: " << metrics.fCapHeight);
// MSG_DEBUG("fAscent: " << metrics.fAscent);
// MSG_DEBUG("fDescent: " << metrics.fDescent);
// MSG_DEBUG("fLeading: " << metrics.fLeading);
// MSG_DEBUG("fXHeight: " << metrics.fXHeight);
MSG_DEBUG("Found " << lines << " lines.");
if (lines > 1 || sr[instance].ww)
{
vector<string> textLines;
if (!sr[instance].ww)
{
textLines = splitLine(sr[instance].te, true);
lines = static_cast<int>(textLines.size());
}
else
{
textLines = splitLine(sr[instance].te, wt, ht, skFont, paint);
lines = static_cast<int>(textLines.size());
}
MSG_DEBUG("Calculated number of lines: " << lines);
int lineHeight = (metrics.fAscent * -1) + metrics.fDescent;
int totalHeight = lineHeight * lines;
/*
if (totalHeight > ht)
{
lines = ht / lineHeight;
totalHeight = lineHeight * lines;
}
*/
MSG_DEBUG("Line height: " << lineHeight << ", total height: " << totalHeight);
vector<string>::iterator iter;
int line = 0;
int maxWidth = 0;
if (textLines.size() > 0)
{
// Calculate the maximum width
for (iter = textLines.begin(); iter != textLines.end(); ++iter)
{
SkRect rect;
skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
if (rect.width() > maxWidth)
maxWidth = rect.width();
}
POSITION_t pos = calcImagePosition(maxWidth, totalHeight, SC_TEXT, instance);
if (!pos.valid)
{
MSG_ERROR("Error calculating the text position!");
TError::setError();
return false;
}
SkScalar lnHt = metrics.fAscent * -1;
for (iter = textLines.begin(); iter != textLines.end(); ++iter)
{
sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
MSG_DEBUG("Trying to print line: " << *iter);
// We want to take care about the horizontal position.
SkRect rect;
skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
SkScalar horizontal = 0.0;
switch(sr[instance].jt)
{
case ORI_BOTTOM_MIDDLE:
case ORI_CENTER_MIDDLE:
case ORI_TOP_MIDDLE:
horizontal = (wt - rect.width()) / 2.0f;
break;
case ORI_BOTTOM_RIGHT:
case ORI_CENTER_RIGHT:
case ORI_TOP_RIGHT:
horizontal = wt - rect.width();
break;
default:
horizontal = pos.left;
}
SkScalar startX = horizontal;
SkScalar startY = (SkScalar)pos.top + (SkScalar)lineHeight * (SkScalar)line;
MSG_DEBUG("x=" << startX << ", y=" << startY);
bool tEffect = false;
// Text effects
if (sr[instance].et > 0)
tEffect = textEffect(&canvas, blob, startX, startY + lnHt, instance);
if (!tEffect)
canvas.drawTextBlob(blob.get(), startX, startY + lnHt, paint);
line++;
if (line > lines)
break;
}
}
}
else // single line
{
string text = sr[instance].te;
sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.data(), skFont);
SkRect rect;
skFont.measureText(text.data(), text.size(), SkTextEncoding::kUTF8, &rect, &paint);
MSG_DEBUG("Calculated Skia rectangle of font: width=" << rect.width() << ", height=" << rect.height());
POSITION_t position;
if (metrics.fCapHeight >= 1.0)
position = calcImagePosition(rect.width(), metrics.fCapHeight, SC_TEXT, instance);
else
position = calcImagePosition(rect.width(), rect.height(), SC_TEXT, instance);
if (!position.valid)
{
MSG_ERROR("Error calculating the text position!");
TError::setError();
return false;
}
MSG_DEBUG("Printing line " << text);
SkScalar startX = (SkScalar)position.left;
SkScalar startY = (SkScalar)position.top;
if (metrics.fCapHeight >= 1.0)
startY += metrics.fCapHeight; // This is the offset of the line
else
startY += rect.height(); // This is the offset of the line
FONT_TYPE sym = TFont::isSymbol(typeFace);
bool tEffect = false;
// Text effects
if (sr[instance].et > 0)
tEffect = textEffect(&canvas, blob, startX, startY, instance);
if (!tEffect && utf8Strlen(text) > 1)
canvas.drawTextBlob(blob.get(), startX, startY, paint);
else
{
int count = 0;
uint16_t *glyphs = nullptr;
if (sym == FT_SYM_MS)
{
MSG_DEBUG("Microsoft proprietary symbol font detected.");
uint16_t *uni;
size_t num = TFont::utf8ToUtf16(text, &uni, true);
MSG_DEBUG("Got " << num << " unichars, first unichar: " << std::hex << std::setw(4) << std::setfill('0') << *uni << std::dec);
if (num > 0)
{
glyphs = new uint16_t[num];
size_t glyphSize = sizeof(uint16_t) * num;
count = skFont.textToGlyphs(uni, num, SkTextEncoding::kUTF16, glyphs, (int)glyphSize);
if (count <= 0)
{
delete[] glyphs;
glyphs = TFont::textToGlyphs(text, typeFace, &num);
count = (int)num;
}
}
else
{
canvas.drawTextBlob(blob.get(), startX, startY, paint);
return true;
}
if (uni)
delete[] uni;
}
else if (tEffect)
return true;
else
{
glyphs = new uint16_t[text.size()];
size_t glyphSize = sizeof(uint16_t) * text.size();
count = skFont.textToGlyphs(text.data(), text.size(), SkTextEncoding::kUTF8, glyphs, (int)glyphSize);
}
if (glyphs && count > 0)
{
MSG_DEBUG("1st glyph: 0x" << std::hex << std::setw(8) << std::setfill('0') << *glyphs << ", # glyphs: " << std::dec << count);
canvas.drawSimpleText(glyphs, sizeof(uint16_t) * count, SkTextEncoding::kGlyphID, startX, startY, skFont, paint);
}
else // Try to print something
{
MSG_WARNING("Got no glyphs! Try to print: " << text);
canvas.drawString(text.data(), startX, startY, skFont, paint);
}
if (glyphs)
delete[] glyphs;
}
}
return true;
}
int TButton::calcLineHeight(const string& text, SkFont& font)
{
DECL_TRACER("TButton::calcLineHeight(const string& text, SkFont& font)");
size_t pos = text.find("\n"); // Search for a line break.
string lText = text;
if (pos != string::npos) // Do we have found a line break?
lText = text.substr(0, pos - 1);// Yes, take only the text up to 1 before the line break (only 1 line).
sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(lText.c_str(), font);
SkRect rect = blob.get()->bounds();
return rect.height();
}
bool TButton::textEffect(SkCanvas *canvas, sk_sp<SkTextBlob>& blob, SkScalar startX, SkScalar startY, int instance)
{
DECL_TRACER("TButton::textEffect(SkBitmap *bm, int instance)");
if (!canvas)
return false;
if (instance < 0 || (size_t)instance >= sr.size())
{
MSG_ERROR("Invalid instance " << instance);
return false;
}
// Drop Shadow
if (sr[instance].et >= 9 && sr[instance].et <= 32)
{
SkScalar gap = 0.0;
SkScalar sigma = 0.0;
SkScalar xDrop = 0.0;
SkScalar yDrop = 0.0;
uint8_t blurAlpha = 255;
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(TColor::getSkiaColor(sr[instance].ct));
// Soft drop shadow
if (sr[instance].et >= 9 && sr[instance].et <= 16)
{
gap = (SkScalar)sr[instance].et - 8.0f;
sigma = 3.0f;
blurAlpha = 127;
}
else if (sr[instance].et >= 17 && sr[instance].et <= 24) // Medium drop shadow
{
gap = (SkScalar)sr[instance].et - 16.0f;
sigma = 2.0f;
blurAlpha = 159;
}
else // Hard drop shadow
{
gap = (SkScalar)sr[instance].et - 24.0f;
sigma = 1.1f;
blurAlpha = 207;
}
xDrop = gap;
yDrop = gap;
SkPaint blur(paint);
blur.setAlpha(blurAlpha);
blur.setColor(TColor::getSkiaColor(sr[instance].ec));
blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, 0));
// blur.setMaskFilter(SkImageFilters::Blur(sigma, sigma, 0));
canvas->drawTextBlob(blob.get(), startX + xDrop, startY + yDrop, blur);
canvas->drawTextBlob(blob.get(), startX, startY, paint);
return true;
}
else if (sr[instance].et >= 5 && sr[instance].et <= 8) // Glow
{
SkScalar sigma = 0.0;
switch(sr[instance].et)
{
case 5: sigma = 2.0; break; // Glow-S
case 6: sigma = 4.0; break; // Glow-M
case 7: sigma = 6.0; break; // Glow-L
case 8: sigma = 8.0; break; // Glow-X
}
SkPaint paint, blur;
paint.setAntiAlias(true);
paint.setColor(TColor::getSkiaColor(sr[instance].ct));
blur.setColor(TColor::getSkiaColor(sr[instance].ec));
blur.setStyle(SkPaint::kStroke_Style);
blur.setStrokeWidth(sigma / 1.5);
blur.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, sigma));
canvas->drawTextBlob(blob.get(), startX, startY, paint);
canvas->drawTextBlob(blob.get(), startX, startY, blur);
return true;
}
else if (sr[instance].et >= 1 && sr[instance].et <= 4) // Outline
{
SkScalar sigma = 0.0;
switch(sr[instance].et)
{
case 1: sigma = 1.0; break; // Outline-S
case 2: sigma = 2.0; break; // Outline-M
case 3: sigma = 4.0; break; // Outline-L
case 4: sigma = 6.0; break; // Outline-X
}
SkPaint paint, outline;
paint.setAntiAlias(true);
paint.setColor(TColor::getSkiaColor(sr[instance].ct));
outline.setAntiAlias(true);
outline.setColor(TColor::getSkiaColor(sr[instance].ec));
outline.setStyle(SkPaint::kStroke_Style);
outline.setStrokeWidth(sigma);
canvas->drawTextBlob(blob.get(), startX, startY, outline);
canvas->drawTextBlob(blob.get(), startX, startY, paint);
return true;
}
return false;
}
/**
* @brief TButton::buttonBorder - draw a border, if any.
* This method draws a border if there is one defined in \b sr[].bs. If there
* is also a global border defined in \b bs then this border is limiting the
* valid borders to it. The method does not check this, because it is subject
* to TPDesign.
*
* @param bm Bitmap to draw the border on.
* @param inst The instance where the border definitition should be taken from
* @param lnType This can be used to define one of the border types
* off, on, drag or drop
*
* @return TRUE on success, otherwise FALSE.
*/
bool TButton::buttonBorder(SkBitmap* bm, int inst, TSystemDraw::LINE_TYPE_t lnType)
{
DECL_TRACER("TButton::buttonBorder(SkBitmap* bm, int instance, TSystemDraw::LINE_TYPE_t lnType)");
TSystemDraw::LINE_TYPE_t lineType = lnType;
int instance = inst;
if (instance < 0)
instance = 0;
else if (static_cast<size_t>(instance) > sr.size())
instance = static_cast<int>(sr.size()) - 1;
if (sr[instance].bs.empty())
{
MSG_DEBUG("No border defined.");
return true;
}
string bname = sr[instance].bs;
// Try to find the border in the system table
if (drawBorder(bm, bname, wt, ht, sr[instance].cb))
return true;
// The border was not found or defined to be not drawn. Therefor we look
// into the system directory (__system/graphics/borders). If the wanted
// border exists there, we're drawing it.
BORDER_t bd;
int numBorders = 0;
if (sr.size() == 2)
{
string n = bname;
if ((StrContains(toLower(n), "inset") || StrContains(n, "active on")) && lineType == TSystemDraw::LT_OFF)
lineType = TSystemDraw::LT_ON;
if (gPageManager->getSystemDraw()->getBorder(bname, lineType, &bd))
numBorders++;
}
else if (lineType == TSystemDraw::LT_OFF && gPageManager->getSystemDraw()->getBorder(bname, TSystemDraw::LT_ON, &bd))
numBorders++;
else if (gPageManager->getSystemDraw()->getBorder(bname, lineType, &bd))
numBorders++;
if (numBorders > 0)
{
SkColor color = TColor::getSkiaColor(sr[instance].cb); // border color
MSG_DEBUG("Button color: #" << std::setw(6) << std::setfill('0') << std::hex << color);
// Load images
SkBitmap imgB, imgBR, imgR, imgTR, imgT, imgTL, imgL, imgBL;
if (!getBorderFragment(bd.b, bd.b_alpha, &imgB, color) || imgB.empty())
return false;
MSG_DEBUG("Got images \"" << bd.b << "\" and \"" << bd.b_alpha << "\" with size " << imgB.info().width() << " x " << imgB.info().height());
if (!getBorderFragment(bd.br, bd.br_alpha, &imgBR, color) || imgBR.empty())
return false;
MSG_DEBUG("Got images \"" << bd.br << "\" and \"" << bd.br_alpha << "\" with size " << imgBR.info().width() << " x " << imgBR.info().height());
if (!getBorderFragment(bd.r, bd.r_alpha, &imgR, color) || imgR.empty())
return false;
MSG_DEBUG("Got images \"" << bd.r << "\" and \"" << bd.r_alpha << "\" with size " << imgR.info().width() << " x " << imgR.info().height());
if (!getBorderFragment(bd.tr, bd.tr_alpha, &imgTR, color) || imgTR.empty())
return false;
MSG_DEBUG("Got images \"" << bd.tr << "\" and \"" << bd.tr_alpha << "\" with size " << imgTR.info().width() << " x " << imgTR.info().height());
if (!getBorderFragment(bd.t, bd.t_alpha, &imgT, color) || imgT.empty())
return false;
MSG_DEBUG("Got images \"" << bd.t << "\" and \"" << bd.t_alpha << "\" with size " << imgT.info().width() << " x " << imgT.info().height());
if (!getBorderFragment(bd.tl, bd.tl_alpha, &imgTL, color) || imgTL.empty())
return false;
MSG_DEBUG("Got images \"" << bd.tl << "\" and \"" << bd.tl_alpha << "\" with size " << imgTL.info().width() << " x " << imgTL.info().height());
if (!getBorderFragment(bd.l, bd.l_alpha, &imgL, color) || imgL.empty())
return false;
mBorderWidth = imgL.info().width();
MSG_DEBUG("Got images \"" << bd.l << "\" and \"" << bd.l_alpha << "\" with size " << imgL.info().width() << " x " << imgL.info().height());
if (!getBorderFragment(bd.bl, bd.bl_alpha, &imgBL, color) || imgBL.empty())
return false;
MSG_DEBUG("Got images \"" << bd.bl << "\" and \"" << bd.bl_alpha << "\" with size " << imgBL.info().width() << " x " << imgBL.info().height());
MSG_DEBUG("Button image size: " << (imgTL.info().width() + imgT.info().width() + imgTR.info().width()) << " x " << (imgTL.info().height() + imgL.info().height() + imgBL.info().height()));
MSG_DEBUG("Total size: " << wt << " x " << ht);
stretchImageWidth(&imgB, wt - imgBL.info().width() - imgBR.info().width());
stretchImageWidth(&imgT, wt - imgTL.info().width() - imgTR.info().width());
stretchImageHeight(&imgL, ht - imgTL.info().height() - imgBL.info().height());
stretchImageHeight(&imgR, ht - imgTR.info().height() - imgBR.info().height());
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()));
// Draw the frame
SkBitmap frame;
allocPixels(bm->info().width(), bm->info().height(), &frame);
frame.eraseColor(SK_ColorTRANSPARENT);
SkCanvas target(*bm, SkSurfaceProps());
SkCanvas canvas(frame, SkSurfaceProps());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
paint.setAntiAlias(true);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgB); // bottom
canvas.drawImage(_image, imgBL.info().width(), ht - imgB.info().height(), SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgT); // top
canvas.drawImage(_image, imgTL.info().width(), 0, SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgBR); // bottom right
canvas.drawImage(_image, wt - imgBR.info().width(), ht - imgBR.info().height(), SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgTR); // top right
canvas.drawImage(_image, wt - imgTR.info().width(), 0, SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgTL); // top left
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgBL); // bottom left
canvas.drawImage(_image, 0, ht - imgBL.info().height(), SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgL); // left
canvas.drawImage(_image, 0, imgTL.info().height(), SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imgR); // right
canvas.drawImage(_image, wt - imgR.info().width(), imgTR.info().height(), SkSamplingOptions(), &paint);
erasePart(bm, frame, Border::ERASE_OUTSIDE, imgL.info().width());
_image = SkImages::RasterFromBitmap(frame);
paint.setBlendMode(SkBlendMode::kSrcATop);
target.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
}
else // We try to draw a frame by forcing it to draw even the not to draw marked frames.
drawBorder(bm, bname, wt, ht, sr[instance].cb, true);
return true;
}
int TButton::numberLines(const string& str)
{
DECL_TRACER("TButton::numberLines(const string& str)");
int lines = 1;
if (str.empty())
return lines;
string::const_iterator iter;
for (iter = str.begin(); iter != str.end(); ++iter)
{
if (*iter == '\n' ||
(type == TEXT_INPUT && dt == "multiple" && *iter == '|') ||
(sr[mActInstance].ww != 0 && *iter == '|'))
lines++;
}
return lines;
}
SkRect TButton::calcRect(int width, int height, int pen)
{
DECL_TRACER("TButton::calcRect(int width, int height, int pen)");
SkRect rect;
SkScalar left = (SkScalar)pen / 2.0;
SkScalar top = (SkScalar)pen / 2.0;
SkScalar w = (SkScalar)width - (SkScalar)pen;
SkScalar h = (SkScalar)height - (SkScalar)pen;
rect.setXYWH(left, top, w, h);
return rect;
}
void TButton::runAnimation()
{
DECL_TRACER("TButton::runAnimation()");
if (mAniRunning)
return;
mAniRunning = true;
int instance = 0;
int max = (int)sr.size();
ulong tm = nu * ru + nd * rd;
while (mAniRunning && !mAniStop && !prg_stopped)
{
mActInstance = instance;
mChanged = true;
if (visible && !drawButton(instance))
break;
instance++;
if (instance >= max)
instance = 0;
std::this_thread::sleep_for(std::chrono::milliseconds(tm));
}
mAniRunning = false;
}
void TButton::runAnimationRange(int start, int end, ulong step)
{
DECL_TRACER("TButton::runAnimationRange(int start, int end, ulong step)");
if (mAniRunning)
return;
mAniRunning = true;
int instance = start - 1;
int max = std::min(end, (int)sr.size());
std::chrono::steady_clock::time_point startt = std::chrono::steady_clock::now();
while (mAniRunning && !mAniStop && !prg_stopped)
{
mActInstance = instance;
mChanged = true;
if (visible)
drawButton(instance); // We ignore the state and try to draw the next instance
instance++;
if (instance >= max)
instance = start - 1;
std::this_thread::sleep_for(std::chrono::milliseconds(step));
if (mAniRunTime > 0)
{
std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
std::chrono::nanoseconds difftime = current - startt;
ulong duration = std::chrono::duration_cast<std::chrono::milliseconds>(difftime).count();
if (duration >= mAniRunTime)
break;
}
}
mAniRunTime = 0;
mAniRunning = false;
}
bool TButton::drawButtonMultistateAni()
{
DECL_TRACER("TButton::drawButtonMultistateAni()");
if (prg_stopped)
return true;
if (!visible || hd) // Do nothing if this button is invisible
return true;
if (mAniRunning || mThrAni.joinable())
{
MSG_TRACE("Animation is already running!");
return true;
}
try
{
mAniStop = false;
mThrAni = thread([=] { runAnimation(); });
mThrAni.detach();
}
catch (exception& e)
{
MSG_ERROR("Error starting the button animation thread: " << e.what());
return false;
}
return true;
}
bool TButton::drawButton(int instance, bool show, bool subview)
{
DECL_TRACER("TButton::drawButton(int instance, bool show, bool subview)");
if (prg_stopped)
return false;
if (subview)
mSubViewPart = subview;
if ((size_t)instance >= sr.size() || instance < 0)
{
MSG_ERROR("Instance " << instance << " is out of bounds!");
TError::setError();
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
if (!visible || hd || instance != mActInstance || !_displayButton)
{
bool db = (_displayButton != nullptr);
MSG_DEBUG("Button " << bi << ", \"" << na << "\" at instance " << instance << " is not to draw!");
MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Hidden: " << (hd ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
TError::clear();
MSG_DEBUG("Drawing button " << bi << ", \"" << na << "\" at instance " << instance);
if (!mChanged && !mLastImage.empty())
{
if (show)
{
showLastButton();
if (type == SUBPAGE_VIEW)
{
if (gPageManager)
gPageManager->showSubViewList(st, this);
}
}
return true;
}
ulong parent = mHandle & 0xffff0000;
getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
// We create an empty (transparent) image here. Later it depends on the
// draw order of the elements. If, for example, the background fill is
// not the first thing, we must be sure to not destroy already drawn
// elemts of the button.
imgButton.eraseColor(SkColors::kTransparent);
bool dynState = false;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, show, &dynState))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (!TTPInit::isTP5() && mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
// If this is a marquee line, don't draw the text. This will be done
// by the surface.
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
continue;
if (!buttonText(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
}
if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[instance].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[instance].oo >= 0)
{
oo = (SkScalar)sr[instance].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
// paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped && !dynState)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
rowBytes = imgButton.info().minRowBytes();
mLastImage = imgButton;
}
#endif
if (show)
{
MSG_DEBUG("Button type: " << buttonTypeToString());
if (type != SUBPAGE_VIEW && !mSubViewPart)
{
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
{
if (gPageManager && gPageManager->getSetMarqueeText())
gPageManager->getSetMarqueeText()(this);
}
}
else if (type != SUBPAGE_VIEW && mSubViewPart)
{
if (gPageManager)
gPageManager->updateSubViewItem(this);
}
}
}
if (!prg_stopped && type == SUBPAGE_VIEW && show)
{
if (gPageManager)
gPageManager->showSubViewList(st, this);
}
return true;
}
bool TButton::drawTextArea(int instance)
{
DECL_TRACER("TButton::drawTextArea(int instance)");
if (prg_stopped)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (!visible || hd)
{
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
if ((size_t)instance >= sr.size() || instance < 0)
{
MSG_ERROR("Instance " << instance << " is out of bounds!");
TError::setError();
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (!mChanged && !mLastImage.empty())
{
showLastButton();
return true;
}
getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!sr[instance].dynamic && !buttonBitmap(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
else if (sr[instance].dynamic && !buttonDynamic(&imgButton, instance, false))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, instance))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
}
if (mGlobalOO >= 0 || sr[instance].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[instance].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[instance].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[instance].oo >= 0)
{
oo = (SkScalar)sr[instance].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
size_t rowBytes = imgButton.info().minRowBytes();
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
size_t rowBytes = imgButton.info().minRowBytes();
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
rowBytes = imgButton.info().minRowBytes();
mLastImage = imgButton;
}
#endif
if (gPageManager && gPageManager->getCallbackInputText())
{
BITMAP_t bm;
bm.buffer = (unsigned char *)imgButton.getPixels();
bm.rowBytes = rowBytes;
bm.left = rleft;
bm.top = rtop;
bm.width = rwidth;
bm.height = rheight;
gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
}
}
return true;
}
bool TButton::drawMultistateBargraph(int level, bool show)
{
DECL_TRACER("TButton::drawMultistateBargraph(int level, bool show)");
if (prg_stopped)
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
if (!visible || hd || !_displayButton)
{
bool db = (_displayButton != nullptr);
MSG_DEBUG("Multistate bargraph " << bi << ", \"" << na << " is not to draw!");
MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
#if TESTMODE == 1
setScreenDone();
#endif
return true;
}
int maxLevel = level;
if (maxLevel > rh)
maxLevel = rh;
else if (maxLevel < rl)
maxLevel = rl;
else if (maxLevel < 0)
maxLevel = rl;
MSG_DEBUG("Display instance " << maxLevel);
ulong parent = mHandle & 0xffff0000;
getDrawOrder(sr[maxLevel]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(&imgButton, maxLevel))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!buttonBitmap(&imgButton, maxLevel))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, maxLevel))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
// If this is a marquee line, don't draw the text. This will be done
// by the surface.
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
continue;
if (!buttonText(&imgButton, maxLevel))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, maxLevel))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
}
}
if (mGlobalOO >= 0 || sr[maxLevel].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
{
#if TESTMODE == 1
setScreenDone();
#endif
return false;
}
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[maxLevel].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[maxLevel].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[maxLevel].oo >= 0)
{
oo = (SkScalar)sr[maxLevel].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
// paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
MSG_DEBUG("Button dimension: " << width << " x " << height);
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
MSG_DEBUG("Old rowBytes: " << rowBytes);
rowBytes = imgButton.info().minRowBytes();
MSG_DEBUG("New rowBytes: " << rowBytes);
mLastImage = imgButton;
}
#endif
if (show)
{
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
{
if (gPageManager && gPageManager->getSetMarqueeText())
gPageManager->getSetMarqueeText()(this);
}
}
#if TESTMODE == 1
else
setScreenDone();
#endif
}
return true;
}
void TButton::setBargraphInvert(int invert)
{
DECL_TRACER("TButton::setBargraphInvert(int invert)");
if (invert < 0 || invert > 3)
return;
if (invert != ri)
{
ri = invert;
mChanged = true;
}
int lastLevel = 0;
int lastJoyX = 0;
int lastJoyY = 0;
if (gPageManager)
{
TButtonStates *buttonStates = gPageManager->getButtonState(type, ap, ad, ch, cp, lp, lv);
if (buttonStates)
{
lastLevel = buttonStates->getLastLevel();
lastJoyX = buttonStates->getLastJoyX();
lastJoyY = buttonStates->getLastJoyY();
}
else
{
MSG_ERROR("Button states not found!");
return;
}
}
if (mChanged && lp && lv)
{
amx::ANET_SEND scmd;
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
if (type == BARGRAPH)
scmd.value = (ri > 0 ? ((rh - rl) - lastLevel) : lastLevel);
else if (invert == 1 || invert == 3)
scmd.value = (ri > 0 ? ((rh - rl) - lastJoyX) : lastJoyX);
scmd.MC = 0x008a;
if (gAmxNet)
gAmxNet->sendCommand(scmd);
if (type == JOYSTICK && (invert == 2 || invert == 3))
{
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri > 0 ? ((rh - rl) - lastJoyY) : lastJoyY);
if (gAmxNet)
gAmxNet->sendCommand(scmd);
}
}
}
void TButton::setBargraphRampDownTime(int t)
{
DECL_TRACER("TButton::setBargraphRampDownTime(int t)");
if (t < 0)
return;
rd = t;
}
void TButton::setBargraphRampUpTime(int t)
{
DECL_TRACER("Button::TButton::setBargraphRampUpTime(int t)");
if (t < 0)
return;
ru = t;
}
void TButton::setBargraphDragIncrement(int inc)
{
DECL_TRACER("TButton::setBargraphDragIncrement(int inc)");
if (inc < 0 || inc > (rh - rl))
return;
rn = inc;
}
/*
* The parameters "x" and "y" are the levels of the x and y axes.
*/
bool TButton::drawJoystick(int x, int y)
{
DECL_TRACER("TButton::drawJoystick(int x, int y)");
if (type != JOYSTICK)
{
MSG_ERROR("Element is no joystick!");
TError::setError();
return false;
}
if (sr.empty())
{
MSG_ERROR("Joystick has no element!");
TError::setError();
return false;
}
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
TError::setError();
return false;
}
int lastJoyX = buttonStates->getLastJoyX();
int lastJoyY = buttonStates->getLastJoyY();
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
if (!mChanged && lastJoyX == x && lastJoyY == y)
{
showLastButton();
return true;
}
if (x < rl)
lastJoyX = rl;
else if (x > rh)
lastJoyX = rh;
else
lastJoyX = x;
if (y < rl)
lastJoyY = rl;
else if (y > rh)
lastJoyY = rh;
else
lastJoyY = y;
buttonStates->setLastJoyX(lastJoyX);
buttonStates->setLastJoyY(lastJoyY);
if (!visible || hd || !_displayButton)
{
bool db = (_displayButton != nullptr);
MSG_DEBUG("Joystick " << bi << ", \"" << na << "\" with coordinates " << lastJoyX << "|" << lastJoyY << " is not to draw!");
MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", callback: " << (db ? "PRESENT" : "N/A"));
return true;
}
ulong parent = mHandle & 0xffff0000;
getDrawOrder(sr[0]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
return false;
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
return false;
imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
bool haveFrame = false;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
{
if (!buttonFill(&imgButton, 0))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!drawJoystickCursor(&imgButton, lastJoyX, lastJoyY))
return false;
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, 0))
return false;
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
if (!buttonText(&imgButton, 0))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, 0))
return false;
haveFrame = true;
}
}
if (mGlobalOO >= 0 || sr[0].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
return false;
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[0].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[0].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[0].oo >= 0)
{
oo = (SkScalar)sr[0].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
// paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped && visible && _displayButton)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
mLastImage = imgButton;
}
#endif
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
}
return true;
}
bool TButton::drawJoystickCursor(SkBitmap *bm, int x, int y)
{
DECL_TRACER("TButton::drawJoystickCursor(SkBitmap *bm, int x, int y)");
if (cd.empty())
return true;
SkBitmap cursor = drawCursorButton(cd, TColor::getSkiaColor(cc));
if (cursor.empty())
return false;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(*bm, SkSurfaceProps());
int imgWidth = cursor.info().width();
int imgHeight = cursor.info().height();
int startX = static_cast<int>(static_cast<double>(wt) / static_cast<double>(rh - rl) * static_cast<double>(x));
int startY = static_cast<int>(static_cast<double>(ht) / static_cast<double>(rh - rl) * static_cast<double>(y));
startX -= imgWidth / 2;
startY -= imgHeight / 2;
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(cursor);
can.drawImage(_image, startX, startY, SkSamplingOptions(), &paint);
return true;
}
SkBitmap TButton::drawCursorButton(const string &cursor, SkColor col)
{
DECL_TRACER("TButton::drawCursorButton(const string &cursor, SkColor col)");
SkBitmap slButton;
// First we look for the cursor button.
if (!gPageManager || !gPageManager->getSystemDraw()->existCursor(cursor))
return slButton;
// There exists one with the wanted name. We grab it and create
// the images from the files.
CURSOR_STYLE_t cst;
if (!gPageManager->getSystemDraw()->getCursor(cursor, &cst)) // should never be true!
{
MSG_ERROR("No cursor entry found!");
return slButton;
}
// Retrieve all available cursor graphics files from the system
CURSOR_t curFiles = gPageManager->getSystemDraw()->getCursorFiles(cst);
if (curFiles.imageBase.empty() && curFiles.imageAlpha.empty())
{
MSG_ERROR("No system cursor graphics found!");
return SkBitmap();
}
// Load the images
SkBitmap imageBase, imageAlpha;
int width = 0;
int height = 0;
bool haveBaseImage = false;
if (!curFiles.imageBase.empty())
{
if (!retrieveImage(curFiles.imageBase, &imageBase))
{
MSG_ERROR("Unable to load image file " << baseName(curFiles.imageBase));
return SkBitmap();
}
width = imageBase.info().width();
height = imageBase.info().height();
haveBaseImage = true;
MSG_DEBUG("Found base image file " << cursor << ".png");
}
if (!curFiles.imageAlpha.empty())
{
if (!retrieveImage(curFiles.imageAlpha, &imageAlpha))
{
MSG_ERROR("Unable to load image file " << baseName(curFiles.imageAlpha));
return SkBitmap();
}
MSG_DEBUG("Found alpha image file " << cursor << "_alpha.png");
if (!haveBaseImage)
{
width = imageAlpha.info().width();
height = imageAlpha.info().height();
if (!allocPixels(width, height, &imageBase))
return imageBase;
imageBase.eraseColor(col);
}
}
if (imageAlpha.empty())
{
MSG_ERROR("Missing alpha mask!");
return imageAlpha;
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
if (!allocPixels(width, height, &slButton))
return slButton;
/*
* The base image, if it exists, contains the final white mask who must be
* on top of the image stack. The stack looks like:
* alpha image (top)
* base image
* target image (bottom)
* where the "target" image is the one where the others are mapped to.
*
* The alpha image contains the cursor in black and white. If there is a
* base image all visible pixels of the base image must be set to the
* cursor color by preventing the original alpha value. All other pixels
* must be marked transparent.
*/
slButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas slCan(slButton, SkSurfaceProps());
if (!haveBaseImage)
{
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
uint32_t *pix = imageBase.getAddr32(x, y);
SkColor color = imageAlpha.getColor(x, y);
SkColor alpha = SkColorGetA(color);
if (!alpha)
*pix = SK_ColorTRANSPARENT;
}
}
}
else
{
// Colorize alpha image
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
uint32_t *pix = imageAlpha.getAddr32(x, y);
SkColor alpha = SkColorGetA(imageAlpha.getColor(x, y));
if (!alpha)
{
*pix = SK_ColorTRANSPARENT;
continue;
}
if (isBigEndian())
*pix = SkColorSetA(col, alpha);
else
{
SkColor red = SkColorGetR(col);
SkColor green = SkColorGetG(col);
SkColor blue = SkColorGetB(col);
*pix = SkColorSetARGB(alpha, blue, green, red);
}
}
}
}
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imageAlpha);
slCan.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
_image = SkImages::RasterFromBitmap(imageBase);
slCan.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
return slButton;
}
bool TButton::drawList(bool show)
{
DECL_TRACER("TButton::drawList(bool show)");
if (!mChanged)
{
showLastButton();
return true;
}
getDrawOrder(sr[0]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
return false;
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
return false;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL)
{
if (!buttonFill(&imgButton, 0))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!sr[0].dynamic && !buttonBitmap(&imgButton, 0))
return false;
else if (sr[0].dynamic && !buttonDynamic(&imgButton, 0, false))
return false;
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, 0))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, 0))
return false;
}
}
if (mGlobalOO >= 0 || sr[0].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
return false;
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[0].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[0].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[0].oo >= 0)
{
oo = (SkScalar)sr[0].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
// paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
size_t rowBytes = imgButton.info().minRowBytes();
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
size_t rowBytes = imgButton.info().minRowBytes();
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
rowBytes = imgButton.info().minRowBytes();
mLastImage = imgButton;
}
#endif
if (show && gPageManager && gPageManager->getCallbackListBox())
{
BITMAP_t bm;
bm.buffer = (unsigned char *)imgButton.getPixels();
bm.rowBytes = rowBytes;
bm.left = rleft;
bm.top = rtop;
bm.width = rwidth;
bm.height = rheight;
gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
}
}
return true;
}
bool TButton::drawBargraph(int instance, int level, bool show)
{
DECL_TRACER("TButton::drawBargraph(int instance, int level, bool show)");
if ((size_t)instance >= sr.size() || instance < 0)
{
MSG_ERROR("Instance " << instance << " is out of bounds!");
TError::setError();
return false;
}
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
return false;
}
int lastLevel = buttonStates->getLastLevel();
if (!mChanged && lastLevel == level)
{
MSG_DEBUG("Drawing unchanged button with level " << level);
showLastButton();
return true;
}
if (level < rl)
lastLevel = rl;
else if (level > rh)
lastLevel = rh;
else
lastLevel = level;
buttonStates->setLastLevel(lastLevel);
int inst = instance;
MSG_DEBUG("drawing bargraph " << lp << ":" << lv << " with level " << lastLevel << " at instance " << inst);
if (!visible || hd || instance != mActInstance || !_displayButton)
{
bool db = (_displayButton != nullptr);
MSG_DEBUG("Bargraph " << bi << ", \"" << na << "\" at instance " << instance << " with level " << lastLevel << " is not to draw!");
MSG_DEBUG("Visible: " << (visible ? "YES" : "NO") << ", Instance/actual instance: " << instance << "/" << mActInstance << ", callback: " << (db ? "PRESENT" : "N/A"));
return true;
}
ulong parent = mHandle & 0xffff0000;
if (type == BARGRAPH)
{
getDrawOrder(sr[1]._do, (DRAW_ORDER *)&mDOrder);
inst = 1;
}
else
getDrawOrder(sr[instance]._do, (DRAW_ORDER *)&mDOrder);
if (TError::isError())
return false;
SkBitmap imgButton;
if (!allocPixels(wt, ht, &imgButton))
return false;
imgButton.eraseColor(TColor::getSkiaColor(sr[0].cf));
bool haveFrame = false;
for (int i = 0; i < ORD_ELEM_COUNT; i++)
{
if (mDOrder[i] == ORD_ELEM_FILL && !haveFrame)
{
if (!buttonFill(&imgButton, (type == BARGRAPH ? 0 : inst)))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BITMAP)
{
if (!barLevel(&imgButton, inst, lastLevel))
return false;
}
else if (mDOrder[i] == ORD_ELEM_ICON)
{
if (!buttonIcon(&imgButton, inst))
return false;
}
else if (mDOrder[i] == ORD_ELEM_TEXT)
{
if (!buttonText(&imgButton, inst))
return false;
}
else if (mDOrder[i] == ORD_ELEM_BORDER)
{
if (!buttonBorder(&imgButton, (type == BARGRAPH ? 0 : inst)))
return false;
haveFrame = true;
}
}
if (mGlobalOO >= 0 || sr[inst].oo >= 0) // Take overall opacity into consideration
{
SkBitmap ooButton;
int w = imgButton.width();
int h = imgButton.height();
if (!allocPixels(w, h, &ooButton))
return false;
SkCanvas canvas(ooButton);
SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
SkRegion region;
region.setRect(irect);
SkScalar oo;
if (mGlobalOO >= 0 && sr[inst].oo >= 0)
{
oo = std::min((SkScalar)mGlobalOO, (SkScalar)sr[inst].oo);
MSG_DEBUG("Set global overal opacity to " << oo);
}
else if (sr[inst].oo >= 0)
{
oo = (SkScalar)sr[inst].oo;
MSG_DEBUG("Set overal opacity to " << oo);
}
else
{
oo = (SkScalar)mGlobalOO;
MSG_DEBUG("Set global overal opacity to " << oo);
}
SkScalar alpha = 1.0 / 255.0 * oo;
MSG_DEBUG("Calculated alpha value: " << alpha);
SkPaint paint;
paint.setAlphaf(alpha);
// paint.setImageFilter(SkImageFilters::AlphaThreshold(region, 0.0, alpha, nullptr));
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgButton);
canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
imgButton.erase(SK_ColorTRANSPARENT, {0, 0, w, h});
imgButton = ooButton;
}
mLastImage = imgButton;
mChanged = false;
if (!prg_stopped && show && visible && instance == mActInstance && _displayButton)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = imgButton.info();
int width = (int)((double)info.width() * gPageManager->getScaleFactor());
int height = (int)((double)info.height() * gPageManager->getScaleFactor());
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImage::MakeFromBitmap(imgButton);
imgButton.allocN32Pixels(width, height);
imgButton.eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(imgButton, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
mLastImage = imgButton;
}
#endif
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
}
return true;
}
POSITION_t TButton::calcImagePosition(int width, int height, CENTER_CODE cc, int number, int line)
{
DECL_TRACER("TButton::calcImagePosition(int with, int height, CENTER_CODE code, int number)");
SR_T act_sr;
POSITION_t position;
int ix, iy, ln;
if (sr.size() == 0)
return position;
if (number <= 0)
act_sr = sr.at(0);
else if ((size_t)number < sr.size())
act_sr = sr.at(number);
else if ((size_t)number >= sr.size())
act_sr = sr.at(sr.size() - 1);
else
return position;
if (line <= 0)
ln = 1;
else
ln = line;
int border_size = getBorderSize(act_sr.bs);
int code, border = border_size;
string dbgCC;
int rwt = 0, rht = 0;
switch (cc)
{
case SC_ICON:
code = act_sr.ji;
ix = act_sr.ix;
iy = act_sr.iy;
border = border_size = 0;
dbgCC = "ICON";
rwt = width;
rht = height;
break;
case SC_BITMAP:
code = act_sr.jb;
ix = act_sr.bx;
iy = act_sr.by;
dbgCC = "BITMAP";
rwt = std::min(wt - border * 2, width);
rht = std::min(ht - border_size * 2, height);
break;
case SC_TEXT:
code = act_sr.jt;
ix = act_sr.tx;
iy = act_sr.ty;
dbgCC = "TEXT";
if (border < 4)
border = 4;
rwt = std::min(wt - border * 2, width); // We've always a minimum (invisible) border of 4 pixels.
rht = std::min(ht - border_size * 2, height); // The height is calculated from a defined border, if any.
break;
}
if (width > rwt || height > rht)
position.overflow = true;
switch (code)
{
case 0: // absolute position
position.left = ix;
position.top = iy;
if (cc == SC_BITMAP && ix < 0 && rwt < width)
position.left *= -1;
if (cc == SC_BITMAP && iy < 0 && rht < height)
position.top += -1;
position.width = rwt;
position.height = rht;
break;
case 1: // top, left
if (cc == SC_TEXT)
{
position.left = border;
position.top = border; // ht - ((ht - rht) / 2) - height * ln;
}
position.width = rwt;
position.height = rht;
break;
case 2: // center, top
if (cc == SC_TEXT)
position.top = border; // ht - ((ht - rht) / 2) - height * ln;
position.left = (wt - rwt) / 2;
position.height = rht;
position.width = rwt;
break;
case 3: // right, top
position.left = wt - rwt;
if (cc == SC_TEXT)
{
position.left = (((position.left - border) < 0) ? 0 : position.left - border);
position.top = border; // ht - (ht - rht) - height * ln;
}
position.width = rwt;
position.height = rht;
break;
case 4: // left, middle
if (cc == SC_TEXT)
{
position.left = border;
position.top = (ht - height) / 2;
}
else
position.top = (ht - rht) / 2;
position.width = rwt;
position.height = rht;
break;
case 6: // right, middle
position.left = wt - rwt;
if (cc == SC_TEXT)
{
position.left = (((position.left - border) < 0) ? 0 : position.left - border);
position.top = (ht - height) / 2;
}
else
position.top = (ht - rht) / 2;
position.width = rwt;
position.height = rht;
break;
case 7: // left, bottom
if (cc == SC_TEXT)
{
position.left = border_size;
position.top = (ht - rht) - height * ln;
}
else
position.top = ht - rht;
position.width = rwt;
position.height = rht;
break;
case 8: // center, bottom
position.left = (wt - rwt) / 2;
if (cc == SC_TEXT)
position.top = (ht - rht) - height * ln;
else
position.top = ht - rht;
position.width = rwt;
position.height = rht;
break;
case 9: // right, bottom
position.left = wt - rwt;
if (cc == SC_TEXT)
{
position.left = (((position.left - border) < 0) ? 0 : position.left - border);
position.top = (ht - rht) - height * ln;
}
else
position.top = ht - rht;
break;
default: // center, middle
position.left = (wt - rwt) / 2;
if (cc == SC_TEXT)
position.top = (ht - height) / 2;
else
position.top = (ht - rht) / 2;
position.width = rwt;
position.height = rht;
}
if (TStreamError::checkFilter(HLOG_DEBUG))
{
string format = getFormatString((ORIENTATION)code);
MSG_DEBUG("Type: " << dbgCC << ", format: " << format <<
", PosType=" << code << ", total height=" << ht << ", height object=" << height <<
", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width <<
", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
}
position.valid = true;
return position;
}
IMAGE_SIZE_t TButton::calcImageSize(int imWidth, int imHeight, int instance, bool aspect)
{
DECL_TRACER("TButton::calcImageSize(int imWidth, int imHeight, bool aspect)");
int border = getBorderSize(sr[instance].bs);
IMAGE_SIZE_t isize;
if (!aspect)
{
isize.width = wt - border * 2;
isize.height = ht - border * 2;
}
else
{
int w = wt - border * 2;
int h = ht - border * 2;
double scale;
if (w < h || imWidth > imHeight)
scale = (double)w / (double)imWidth;
else
scale = (double)h / (double)imHeight;
isize.width = (int)((double)imWidth * scale);
isize.height = (int)((double)imHeight * scale);
}
MSG_DEBUG("Sizing image: Original: " << imWidth << " x " << imHeight << " to " << isize.width << " x " << isize.height);
return isize;
}
string TButton::getFormatString(ORIENTATION to)
{
DECL_TRACER("TButton::getFormatString(CENTER_CODE cc)");
switch(to)
{
case ORI_ABSOLUT: return "ABSOLUT";
case ORI_BOTTOM_LEFT: return "BOTTOM/LEFT";
case ORI_BOTTOM_MIDDLE: return "BOTTOM/MIDDLE";
case ORI_BOTTOM_RIGHT: return "BOTTOM/RIGHT";
case ORI_CENTER_LEFT: return "CENTER/LEFT";
case ORI_CENTER_MIDDLE: return "CENTER/MIDDLE";
case ORI_CENTER_RIGHT: return "CENTER/RIGHT";
case ORI_TOP_LEFT: return "TOP/LEFT";
case ORI_TOP_MIDDLE: return "TOP/MIDDLE";
case ORI_TOP_RIGHT: return "TOP/RIGHT";
case ORI_SCALE_FIT: return "SCALE/FIT";
case ORI_SCALE_ASPECT: return "SCALE/ASPECT";
}
return "UNKNOWN"; // Should not happen!
}
int TButton::getBorderSize(const std::string& name)
{
DECL_TRACER("TButton::getBorderSize(const std::string& name)");
int width = getBorderWidth(name);
if (width > 0)
return width;
if (gPageManager && gPageManager->getSystemDraw())
{
if (gPageManager->getSystemDraw()->existBorder(name))
return gPageManager->getSystemDraw()->getBorderWidth(name);
}
return 0;
}
void TButton::setUserName(const string& user)
{
DECL_TRACER("TButton::setUserName(const string& user)");
if (TConfig::getUserPassword(user).empty())
return;
mUser = user;
}
void TButton::calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)
{
DECL_TRACER("TButton::clacImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY)");
int spX = btWidth - (btFrame * 2);
int spY = btHeight - (btFrame * 2);
if (imWidth <= spX && imHeight <= spY)
{
*realX = imWidth;
*realY = imHeight;
return;
}
int oversizeX = 0, oversizeY = 0;
if (imWidth > spX)
oversizeX = imWidth - spX;
if (imHeight > spY)
oversizeY = imHeight - spY;
double percent = 0.0;
if (oversizeX > oversizeY)
percent = 100.0 / (double)imWidth * (double)spX;
else
percent = 100.0 / (double)imHeight * (double)spY;
*realX = (int)(percent / 100.0 * (double)imWidth);
*realY = (int)(percent / 100.0 * (double)imHeight);
}
SkBitmap TButton::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
{
DECL_TRACER("TButton::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
if (width <= 0 || height <= 0)
{
MSG_WARNING("Got invalid width of height! (width: " << width << ", height: " << height << ")");
return SkBitmap();
}
if (imgRed.empty())
{
MSG_WARNING("Missing mask to draw image!");
return SkBitmap();
}
SkPixmap pixmapRed = imgRed.pixmap();
SkPixmap pixmapMask;
bool haveBothImages = true;
if (!imgMask.empty())
pixmapMask = imgMask.pixmap();
else
haveBothImages = false;
SkBitmap maskBm;
if (!allocPixels(width, height, &maskBm))
return SkBitmap();
maskBm.eraseColor(SK_ColorTRANSPARENT);
for (int ix = 0; ix < width; ix++)
{
for (int iy = 0; iy < height; iy++)
{
SkColor pixelRed;
SkColor pixelMask;
if (ix < pixmapRed.info().width() && iy < pixmapRed.info().height())
pixelRed = pixmapRed.getColor(ix, iy);
else
pixelRed = 0;
if (haveBothImages && !imgMask.empty() &&
ix < pixmapMask.info().width() && iy < pixmapMask.info().height())
pixelMask = pixmapMask.getColor(ix, iy);
else
pixelMask = SkColorSetA(SK_ColorWHITE, 0);
SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
uint32_t alpha = SkColorGetA(pixel);
uint32_t *wpix = nullptr;
if (ix < maskBm.info().width() && iy < maskBm.info().height())
wpix = maskBm.getAddr32(ix, iy);
if (!wpix)
continue;
if (alpha == 0)
pixel = pixelMask;
*wpix = pixel;
}
}
return maskBm;
}
/**
* @brief Takes 2 images and combines them to one.
*
* The 2 images are a solid base image defining the basic form and an identical
* image defining the alpha channel.
*
* @param base The base image containing the form as black pixels.
* @param alpha The image containing just an alpha channel.
* @param col The color which should be used instead of a black pixel.
*
* @return On success a valid bitmap is returned containing the form.
* On error an empty bitmap is returned.
*/
SkBitmap TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)
{
DECL_TRACER("TButton::combineImages(SkBitmap& base, SkBitmap& alpha, SkColor col)");
int width = base.info().width();
int height = base.info().height();
SkBitmap Bm; // The new bitmap. It will be returned in the end.
if (width != alpha.info().width() || height != alpha.info().height())
{
MSG_ERROR("Mask and alpha have different size! [ " << width << " x " << height << " to " << alpha.info().width() << " x " << alpha.info().height());
return Bm;
}
if (!allocPixels(width, height, &Bm))
return Bm;
Bm.eraseColor(SK_ColorTRANSPARENT);
for (int ix = 0; ix < width; ix++)
{
for (int iy = 0; iy < height; iy++)
{
SkColor pixelAlpha = alpha.getColor(ix, iy);
uint32_t *bpix = Bm.getAddr32(ix, iy);
uchar al = SkColorGetA(pixelAlpha);
uchar red = SkColorGetR(col);
uchar green = SkColorGetG(col);
uchar blue = SkColorGetB(col);
if (pixelAlpha == 0)
red = green = blue = 0;
// Skia reads image files in the natural byte order of the CPU.
// While on Intel CPUs the byte order is little endian it is
// mostly big endian on other CPUs. This means that the order of
// the colors is RGB on big endian CPUs (ARM, ...) and BGR on others.
// To compensate this, we check the endianess of the CPU and set
// the byte order according.
if (isBigEndian())
*bpix = SkColorSetARGB(al, blue, green, red);
else
*bpix = SkColorSetARGB(al, red, green, blue);
}
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(Bm);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(base);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
return Bm;
}
/**
* @brief TButton::colorImage: Colorize frame element
* This method colorizes a frame element. If there is, beside the base picture,
* also a an alpha mask picture present, the elemnt is colorized by taking the
* mask to find the pixels to colorize.
* Otherwise the pixel is melted with the target color. This means a pseudo mask
* is used.
*
* @param base This is the base image and must be present.
* @param alpha This is optional alpha mask. If present it is used to
* define the alpha value of the pixels.
* @param col This is the color to be used.
* @param bg This is the background color to be used on the transparent
* pixels inside an element. On the transparent pixels on the
* outside of the element the pixel is set to transparent.
* @param useBG If this is TRUE, all transparent pixels are set to the
* background color \b bg.
* @return
* On success a new image containing the colorized element is returned.
* Otherwise an empty image is returned.
*/
SkBitmap TButton::colorImage(SkBitmap& base, SkBitmap& alpha, SkColor col, SkColor bg, bool useBG)
{
DECL_TRACER("TButton::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)");
int width = base.info().width();
int height = base.info().height();
if (width <= 0 || height <= 0)
{
MSG_WARNING("Got invalid width or height! (width: " << width << ", height: " << height << ")");
return SkBitmap();
}
if (!alpha.empty())
{
if (width != alpha.info().width() || height != alpha.info().height())
{
MSG_ERROR("Base and alpha masks have different size!");
return SkBitmap();
}
}
SkBitmap maskBm;
if (!allocPixels(width, height, &maskBm))
return SkBitmap();
maskBm.eraseColor(SK_ColorTRANSPARENT);
for (int ix = 0; ix < width; ix++)
{
for (int iy = 0; iy < height; iy++)
{
SkColor pixelAlpha = 0;
if (!alpha.empty())
pixelAlpha = alpha.getColor(ix, iy);
else
pixelAlpha = base.getColor(ix, iy);
uint32_t *wpix = maskBm.getAddr32(ix, iy);
if (!wpix)
{
MSG_ERROR("No pixel buffer!");
break;
}
uint32_t ala = SkColorGetA(pixelAlpha);
if (ala == 0 && !useBG)
pixelAlpha = SK_ColorTRANSPARENT;
else if (ala == 0)
pixelAlpha = bg;
else
{
uint32_t red = SkColorGetR(col);
uint32_t green = SkColorGetG(col);
uint32_t blue = SkColorGetB(col);
pixelAlpha = SkColorSetARGB(ala, red, green, blue);
}
*wpix = pixelAlpha;
}
}
if (!alpha.empty())
{
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrcOver);
SkCanvas can(maskBm);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(base);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
}
return maskBm;
}
bool TButton::retrieveImage(const string& path, SkBitmap* image)
{
DECL_TRACER("TButton::retrieveImage(const string& path, SkBitmap* image)");
if (path.empty() || !image)
{
MSG_WARNING("TButton::retrieveImage: Empty parameter!");
return false;
}
if (!fs::exists(path) || !fs::is_regular_file(path))
{
MSG_WARNING("File \"" << path << "\" does not exist or is not a regular file!");
return false;
}
sk_sp<SkData> im;
if (!(im = readImage(path)))
return false;
DecodeDataToBitmap(im, image);
if (image->empty())
{
MSG_WARNING("Could not create the image " << path);
return false;
}
return true;
}
/**
* @brief getBorderFragment - get part of border
* The method reads a border image fragment from the disk and converts it to
* the border color. If there is a base image and an alpha mask image, the
* pixels of the alpha mask are converted to the border color and then the base
* image is layed over the mask image.
* In case there is no base image, an image with the same size as the mask image
* is created and filled transparaent.
*
* @param path The path and file name of the base image.
* @param pathAlpha The path and file name of the alpha mask image.
* @param image A pointer to an empty bitmap.
* @param color The border color
*
* @return In case the images exists and were loaded successfully, TRUE is
* returned.
*/
bool TButton::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)
{
DECL_TRACER("TButton::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)");
if (!image)
{
MSG_ERROR("Invalid pointer to image!");
return false;
}
sk_sp<SkData> im;
SkBitmap bm;
bool haveBaseImage = false;
SkColor swCol = color;
if (!isBigEndian())
flipColorLevelsRB(swCol);
// If the path ends with "alpha.png" then it is a mask image. This not what
// we want first unless this is the only image available.
if (!endsWith(path, "alpha.png") || pathAlpha.empty())
{
if (!path.empty() && retrieveImage(path, image))
{
haveBaseImage = true;
// Underly the pixels with the border color
MSG_DEBUG("Path: " << path << ", pathAlpha: " << pathAlpha);
if (pathAlpha.empty() || !fs::exists(pathAlpha) || path == pathAlpha)
{
SkImageInfo info = image->info();
SkBitmap b;
allocPixels(info.width(), info.height(), &b);
b.eraseColor(SK_ColorTRANSPARENT);
for (int x = 0; x < info.width(); ++x)
{
for (int y = 0; y < info.height(); ++y)
{
SkColor alpha = SkColorGetA(image->getColor(x, y));
uint32_t *pix = b.getAddr32(x, y);
if (alpha > 0)
*pix = swCol;
}
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setBlendMode(SkBlendMode::kDstATop);
SkCanvas can(*image);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(b);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
}
}
}
// If there is no valid path return.
if (pathAlpha.empty())
return haveBaseImage;
// On error retrieving the image, return.
if (!retrieveImage(pathAlpha, &bm))
return haveBaseImage;
// If there was no base image loaded, allocate the space for an image
// filled transparent. Make it the same size as the mask image.
if (!haveBaseImage)
{
allocPixels(bm.info().width(), bm.info().height(), image);
image->eraseColor(SK_ColorTRANSPARENT);
}
// Only if the base image and the mask image have the same size, which
// should be the case, then the visible pixels of the mask image are
// colored by the border color.
if (image->info().dimensions() == bm.info().dimensions())
{
for (int y = 0; y < image->info().height(); ++y)
{
for (int x = 0; x < image->info().width(); ++x)
{
SkColor col = bm.getColor(x, y);
SkColor alpha = SkColorGetA(col);
uint32_t *pix = bm.getAddr32(x, y);
if (alpha == 0)
*pix = SK_ColorTRANSPARENT;
else
*pix = SkColorSetA(swCol, alpha);
}
}
}
// Here we draw the border fragment over the base image.
SkPaint paint;
paint.setAntiAlias(true);
paint.setBlendMode(SkBlendMode::kDstATop);
SkCanvas can(*image);
sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bm);
can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
return true;
}
void TButton::show()
{
DECL_TRACER("TButton::show()");
// First we detect whether we have a dynamic button or not.
// To do this, we find out the current active instance.
int inst = 0;
if (mActInstance >= 0 && (size_t)mActInstance < sr.size())
inst = mActInstance;
// If the dynamic flag is not set and we have already an image of the
// button, we send just the saved image to the screen.
if (visible && !mChanged && !sr[inst].dynamic && !mLastImage.empty())
{
showLastButton();
return;
}
// Here the button, or the active instance was never drawn or it is a
// dynamic button. Then the button must be drawn.
visible = true;
makeElement();
if (isSystemButton() && !mSystemReg)
registerSystemButton();
}
void TButton::showLastButton()
{
DECL_TRACER("TButton::showLastButton()");
if (mLastImage.empty())
{
#if TESTMODE == 1
setScreenDone();
#endif
return;
}
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
if (!prg_stopped && visible)
{
ulong parent = mHandle & 0xffff0000;
size_t rowBytes = mLastImage.info().minRowBytes();
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
}
#endif
if (type == TEXT_INPUT)
{
if (gPageManager && gPageManager->getCallbackInputText())
{
BITMAP_t bm;
bm.buffer = (unsigned char *)mLastImage.getPixels();
bm.rowBytes = rowBytes;
bm.left = rleft;
bm.top = rtop;
bm.width = rwidth;
bm.height = rheight;
gPageManager->getCallbackInputText()(this, bm, mBorderWidth);
}
}
else if (type == LISTBOX)
{
if (gPageManager && gPageManager->getCallbackListBox())
{
BITMAP_t bm;
bm.buffer = (unsigned char *)mLastImage.getPixels();
bm.rowBytes = rowBytes;
bm.left = rleft;
bm.top = rtop;
bm.width = rwidth;
bm.height = rheight;
gPageManager->getCallbackListBox()(this, bm, mBorderWidth);
}
}
else if (type == SUBPAGE_VIEW)
{
if (gPageManager && gPageManager->getDisplayViewButton())
{
TBitmap image((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
TColor::COLOR_T bgcolor = TColor::getAMXColor(sr[mActInstance].cf);
gPageManager->getDisplayViewButton()(mHandle, getParent(), (on.empty() ? false : true), image, wt, ht, mPosLeft, mPosTop, sa, bgcolor);
}
}
else if (_displayButton)
{
TBitmap image((unsigned char *)mLastImage.getPixels(), mLastImage.info().width(), mLastImage.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
{
if (gPageManager && gPageManager->getSetMarqueeText())
gPageManager->getSetMarqueeText()(this);
}
}
mChanged = false;
}
}
void TButton::hide(bool total)
{
DECL_TRACER("TButton::hide()");
// if (type == MULTISTATE_GENERAL && ar == 1)
// mAniStop = true;
if (!prg_stopped && total)
{
int rwidth = wt;
int rheight = ht;
int rleft = mPosLeft;
int rtop = mPosTop;
ulong parent = mHandle & 0xffff0000;
THR_REFRESH_t *tr = _findResource(mHandle, parent, bi);
if (tr && tr->mImageRefresh)
{
if (tr->mImageRefresh->isRunning())
tr->mImageRefresh->stop();
}
#ifdef _SCALE_SKIA_
if (gPageManager && gPageManager->getScaleFactor() != 1.0)
{
rwidth = (int)((double)wt * gPageManager->getScaleFactor());
rheight = (int)((double)ht * gPageManager->getScaleFactor());
rleft = (int)((double)mPosLeft * gPageManager->getScaleFactor());
rtop = (int)((double)mPosTop * gPageManager->getScaleFactor());
}
#endif
if (type == TEXT_INPUT)
{
if (gPageManager && gPageManager->getCallDropButton())
gPageManager->getCallDropButton()(mHandle);
visible = false;
return;
}
SkBitmap imgButton;
if (rwidth < 0 || rheight < 0)
{
MSG_ERROR("Invalid size of image: " << rwidth << " x " << rheight);
return;
}
try
{
if (!allocPixels(wt, ht, &imgButton))
return;
imgButton.eraseColor(SK_ColorTRANSPARENT);
}
catch (std::exception& e)
{
MSG_ERROR("Error creating image: " << e.what());
visible = false;
return;
}
if (!_displayButton && gPageManager)
_displayButton = gPageManager->getCallbackDB();
if (_displayButton)
{
TBitmap image((unsigned char *)imgButton.getPixels(), imgButton.info().width(), imgButton.info().height());
_displayButton(mHandle, parent, image, rwidth, rheight, rleft, rtop, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
mChanged = false;
}
}
visible = false;
}
bool TButton::isClickable(int x, int y)
{
DECL_TRACER("TButton::isClickable()");
if (mEnabled && /*((cp != 0 && ch != 0) || (lp != 0 && lv != 0) || !cm.empty() || !op.empty() || !pushFunc.empty() || isSystemButton()) &&*/ hs.compare("passThru") != 0)
{
if (x != -1 && y != -1 && hs.empty() && !mLastImage.empty() && isPixelTransparent(x, y))
return false;
return true;
}
return false;
}
/**
* Handling of system button "connection state". It consists of 12 states
* indicating the network status. The states have the following meaning:
*
* 0 Diconnected (never was connected before since startup)
* 1 - 6 Connected (blink may be shown with dark and light green)
* 7, 8 Disconnected (timeout or loss of connection)
* 9 - 11 Connection in progress
*/
void TButton::funcNetwork(int state)
{
DECL_TRACER("TButton::funcNetwork(int state)");
TButtonStates *buttonStates = getButtonState();
if (buttonStates)
buttonStates->setLastLevel(state);
mActInstance = state;
mChanged = true;
if (visible)
makeElement(state);
}
/**
* Handling the timer event from the controller. This comes usualy every
* 20th part of a second (1 second / 20)
*/
void TButton::funcTimer(const amx::ANET_BLINK& blink)
{
DECL_TRACER("TButton::funcTimer(const amx::ANET_BLINK& blink)");
string tm;
std::stringstream sstr;
switch (ad)
{
case 141: // Standard time
sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
<< std::setw(2) << std::setfill('0') << (int)blink.minute << ":"
<< std::setw(2) << std::setfill('0') << (int)blink.second;
mLastBlink = blink;
break;
case 142: // Time AM/PM
{
int hour = (blink.hour > 12) ? (blink.hour - 12) : blink.hour;
sstr << std::setw(2) << std::setfill('0') << hour << ":"
<< std::setw(2) << std::setfill('0') << (int)blink.minute << " ";
if (blink.hour <= 12)
sstr << "AM";
else
sstr << "PM";
mLastBlink = blink;
}
break;
case 143: // Time 24 hours
sstr << std::setw(2) << std::setfill('0') << (int)blink.hour << ":"
<< std::setw(2) << std::setfill('0') << (int)blink.minute;
mLastBlink = blink;
break;
case 151: // Weekday
switch (blink.weekday)
{
case 0: sstr << "Monday"; break;
case 1: sstr << "Tuesday"; break;
case 2: sstr << "Wednesday"; break;
case 3: sstr << "Thursday"; break;
case 4: sstr << "Friday"; break;
case 5: sstr << "Saturday"; break;
case 6: sstr << "Sunday"; break;
}
break;
case 152: // Date mm/dd
sstr << (int)blink.month << "/" << (int)blink.day;
break;
case 153: // Date dd/mm
sstr << (int)blink.day << "/" << (int)blink.month;
break;
case 154: // Date mm/dd/yyyy
sstr << (int)blink.month << "/" << (int)blink.day << "/" << (int)blink.year;
break;
case 155: // Date dd/mm/yyyy
sstr << blink.day << "/" << blink.month << "/" << blink.year;
break;
case 156: // Date month dd/yyyy
switch (blink.month)
{
case 1: sstr << "January"; break;
case 2: sstr << "February"; break;
case 3: sstr << "March"; break;
case 4: sstr << "April"; break;
case 5: sstr << "May"; break;
case 6: sstr << "June"; break;
case 7: sstr << "July"; break;
case 8: sstr << "August"; break;
case 9: sstr << "September"; break;
case 10: sstr << "October"; break;
case 11: sstr << "November"; break;
case 12: sstr << "December"; break;
}
sstr << " " << (int)blink.day << "/" << (int)blink.year;
break;
case 157: // Date dd month yyyy
sstr << (int)blink.day;
switch (blink.month)
{
case 1: sstr << "January"; break;
case 2: sstr << "February"; break;
case 3: sstr << "March"; break;
case 4: sstr << "April"; break;
case 5: sstr << "May"; break;
case 6: sstr << "June"; break;
case 7: sstr << "July"; break;
case 8: sstr << "August"; break;
case 9: sstr << "September"; break;
case 10: sstr << "October"; break;
case 11: sstr << "November"; break;
case 12: sstr << "December"; break;
}
sstr << " " << (int)blink.year;
break;
case 158: // Date yyyy-mm-dd
sstr << (int)blink.year << "-" << (int)blink.month << "-" << (int)blink.day;
break;
default:
return;
}
vector<SR_T>::iterator iter;
tm = sstr.str();
for (iter = sr.begin(); iter != sr.end(); ++iter)
iter->te = tm;
mChanged = true;
if (visible)
makeElement(mActInstance);
}
bool TButton::isPixelTransparent(int x, int y)
{
DECL_TRACER("TButton::isPixelTransparent(int x, int y)");
// If there is no image we treat it as a non transpararent pixel.
if (sr[mActInstance].mi.empty() && sr[mActInstance].bm.empty())
return false;
// The mLastImage must never be empty! Although this should never be true,
// we treat it as a transparent pixel if it happen.
if (mLastImage.empty())
{
MSG_ERROR("Internal error: No image for button available!");
return true;
}
// Make sure the coordinates are inside the bounds. A test for a pixel
// outside the bounds would lead in an immediate exit because of an assert
// test in skia. Although this should not happen, we treat the pixel as
// transparent in this case, because the coordinates didn't hit the button.
if (x < 0 || x >= mLastImage.info().width() || y < 0 || y >= mLastImage.info().height())
{
MSG_ERROR("The X or Y coordinate is out of bounds!");
MSG_ERROR("X=" << x << ", Y=" << y << ", width=" << mLastImage.info().width() << ", height=" << mLastImage.info().height());
return true;
}
float alpha = mLastImage.getAlphaf(x, y); // Get the alpha value (0.0 to 1.0)
if (alpha != 0.0)
return false;
return true;
}
bool TButton::checkForSound()
{
DECL_TRACER("TButton::checkForSound()");
vector<SR_T>::iterator iter;
for (iter = sr.begin(); iter != sr.end(); ++iter)
{
if (!iter->sd.empty())
return true;
}
return false;
}
bool TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)
{
DECL_TRACER("TButton::scaleImage(SkBitmap *bm, double scaleWidth, double scaleHeight)");
if (!bm)
return false;
if (scaleWidth == 1.0 && scaleHeight == 1.0)
return true;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
// paint.setFilterQuality(kHigh_SkFilterQuality);
// Calculate new dimension
SkImageInfo info = bm->info();
int width = std::max(1, (int)((double)info.width() * scaleWidth));
int height = std::max(1, (int)((double)info.height() * scaleHeight));
MSG_DEBUG("Scaling image to size " << width << " x " << height);
// Create a canvas and draw new image
sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
if (!allocPixels(width, height, bm))
return false;
bm->eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(*bm, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, width, height);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
return true;
}
bool TButton::stretchImageWidth(SkBitmap *bm, int width)
{
DECL_TRACER("TButton::stretchImageWidth(SkBitmap *bm, int width)");
if (!bm)
return false;
int rwidth = width;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
// paint.setFilterQuality(kHigh_SkFilterQuality);
SkImageInfo info = bm->info();
sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
if (width <= 0)
rwidth = info.width() + width;
if (rwidth <= 0)
rwidth = 1;
MSG_DEBUG("Width: " << rwidth << ", Height: " << info.height());
if (!allocPixels(rwidth, info.height(), bm))
return false;
bm->eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(*bm, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, info.height());
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
return true;
}
bool TButton::stretchImageHeight(SkBitmap *bm, int height)
{
DECL_TRACER("TButton::stretchImageHeight(SkBitmap *bm, int height)");
if (!bm)
return false;
int rheight = height;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
// paint.setFilterQuality(kHigh_SkFilterQuality);
SkImageInfo info = bm->info();
if (height <= 0)
rheight = info.height() + height;
if (rheight <= 0)
rheight = 1;
sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
MSG_DEBUG("Width: " << info.width() << ", Height: " << rheight);
if (!allocPixels(info.width(), rheight, bm))
return false;
bm->eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(*bm, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, info.width(), rheight);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
return true;
}
bool TButton::stretchImageWH(SkBitmap *bm, int width, int height)
{
DECL_TRACER("TButton::stretchImageWH(SkBitmap *bm, int width, int height)");
if (!bm)
return false;
int rwidth = width;
int rheight = height;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
// paint.setFilterQuality(kHigh_SkFilterQuality);
SkImageInfo info = bm->info();
if (width <= 0)
rwidth = info.width() + width;
if (height <= 0)
rheight = info.height() + height;
if (rheight <= 0)
rheight = 1;
if (rwidth <= 0)
rwidth = 1;
sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
MSG_DEBUG("Width: " << rwidth << ", Height: " << rheight);
if (!allocPixels(rwidth, rheight, bm))
return false;
bm->eraseColor(SK_ColorTRANSPARENT);
SkCanvas can(*bm, SkSurfaceProps());
SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, rheight);
can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
return true;
}
/**
* This button got the click because it matches the coordinates of a mouse
* click. It checkes whether it is clickable or not. If it is clickable, it
* depends on the type of element what happens.
*/
bool TButton::doClick(int x, int y, bool pressed)
{
DECL_TRACER("TButton::doClick(int x, int y, bool pressed)");
if (!isClickable(x, y))
return false;
amx::ANET_SEND scmd;
int instance = 0;
int sx = x, sy = y;
bool isSystem = isSystemButton();
int lastLevel = 0;
int lastJoyX = 0;
int lastJoyY = 0;
int lastSendLevelX = 0;
int lastSendLevelY = 0;
TButtonStates *buttonStates = getButtonState();
if (buttonStates)
{
lastLevel = buttonStates->getLastLevel();
lastJoyX = buttonStates->getLastJoyX();
lastJoyY = buttonStates->getLastJoyY();
lastSendLevelX = buttonStates->getLastSendLevelX();
lastSendLevelY = buttonStates->getLastSendLevelY();
}
else
{
MSG_ERROR("Button states not found!");
return false;
}
if (pressed && gPageManager && !checkForSound() && (ch > 0 || lv > 0 || !pushFunc.empty() || isSystem))
{
TSystemSound sysSound(TConfig::getSystemPath(TConfig::SOUNDS));
if (gPageManager->havePlaySound() && sysSound.getSystemSoundState())
gPageManager->getCallPlaySound()(sysSound.getTouchFeedbackSound());
}
#ifdef _SCALE_SKIA_
// To be able to test a button for a transparent pixel, we must scale
// the coordinates because the test is made on the last drawn image and
// this image is scaled (if scaling is activated).
if (TConfig::getScale() && gPageManager && gPageManager->getScaleFactor() != 1.0)
{
double scaleFactor = gPageManager->getScaleFactor();
sx = (int)((double)x * scaleFactor);
sy = (int)((double)y * scaleFactor);
}
#endif
// Handle system buttons. Here the system keyboard buttons are handled.
if (_buttonPress && mActInstance >= 0 && static_cast<size_t>(mActInstance) < sr.size() && cp == 0 && ch > 0)
{
// Handling the keyboard buttons is very expensive. To not block too
// long, we let it run in a separate thread.
std::thread thr = std::thread([=] { _buttonPress(ch, static_cast<uint>(mHandle), pressed); });
thr.detach();
}
// If the button is marked as password protected, then we must display
// a window with an input line to get the password from the user. Only if
// the password is equal to the password in the setup the button is
// processed further.
if (pressed && (pp > 0 || !mUser.empty()))
{
if (!mPassword.empty())
{
if (mPassword[0] == 1) // No or invalid password?
{ // Yes, then clear it and return
mPassword.clear();
return false;
}
string pass;
if (!mUser.empty())
pass = TConfig::getUserPassword(mUser);
if (pass.empty() && pp > 0)
{
switch(pp)
{
case 1: pass = TConfig::getPassword1(); break;
case 2: pass = TConfig::getPassword2(); break;
case 3: pass = TConfig::getPassword3(); break;
case 4: pass = TConfig::getPassword4(); break;
default:
MSG_WARNING("Detected invalid password index " << pp);
mPassword.clear();
return false;
}
}
if (pass != mPassword) // Does the password not match?
{ // Don't match then clear it and return
MSG_PROTOCOL("User typed wrong password!");
mPassword.clear();
return false;
}
// The password match. We clear it and proceed.
mPassword.clear();
}
else if (gPageManager && gPageManager->getAskPassword())
{
string msg;
if (mUser.empty())
msg = "Enter [" + intToString(pp) + "] password";
else
msg = "Enter password for user " + mUser;
mPassword.clear();
gPageManager->getAskPassword()(mHandle, msg, "Password", x, y);
return true;
}
else
return false;
}
if (type == GENERAL)
{
MSG_DEBUG("Button type: GENERAL; System button: " << (isSystem ? "YES" : "NO") << "; CH: " << cp << ":" << ch << "; AD: " << ap << ":" << ad);
if (isSystem && ch == SYSTEM_ITEM_SOUNDSWITCH) // Button sounds on/off
{
if (pressed)
{
MSG_TRACE("System button sounds are toggled ...");
TConfig::setTemporary(false);
bool sstate = TConfig::getSystemSoundState();
if (sstate)
mActInstance = instance = 0;
else
mActInstance = instance = 1;
TConfig::saveSystemSoundState(!sstate);
TConfig::saveSettings();
mChanged = true;
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SETUPPAGE) // Enter setup page
{
if (pressed)
{
if (gPageManager && gPageManager->haveSetupPage())
gPageManager->callSetupPage();
}
}
else if (isSystem && ch == SYSTEM_ITEM_SHUTDOWN) // Shutdown program
{
if (pressed)
{
if (gPageManager && gPageManager->haveShutdown())
gPageManager->callShutdown();
}
}
else if (isSystem && ch == SYSTEM_ITEM_VOLUMEUP) // System volume up
{
TConfig::setTemporary(true);
int vol = TConfig::getSystemVolume() + 10;
if (vol > 100)
vol = 100;
if (pressed)
TConfig::saveSystemVolume(vol);
if (pressed)
mActInstance = instance = 1;
else
mActInstance = instance = 0;
mChanged = true;
drawButton(mActInstance, true);
if (pressed && gPageManager)
{
int channel = TConfig::getChannel();
int system = TConfig::getSystem();
amx::ANET_COMMAND cmd;
cmd.MC = 0x000a;
cmd.device1 = channel;
cmd.port1 = 0;
cmd.system = system;
cmd.data.message_value.system = system;
cmd.data.message_value.value = 9; // System volume
cmd.data.message_value.content.integer = vol;
cmd.data.message_value.device = channel;
cmd.data.message_value.port = 0; // Must be the address port of button
cmd.data.message_value.type = 0x20; // Unsigned int
gPageManager->doCommand(cmd);
}
}
else if (isSystem && ch == SYSTEM_ITEM_VOLUMEDOWN) // System volume down
{
TConfig::setTemporary(true);
int vol = TConfig::getSystemVolume() - 10;
if (vol < 0)
vol = 0;
if (pressed)
TConfig::saveSystemVolume(vol);
if (pressed)
mActInstance = instance = 1;
else
mActInstance = instance = 0;
mChanged = true;
drawButton(mActInstance, true);
if (pressed && gPageManager)
{
int channel = TConfig::getChannel();
int system = TConfig::getSystem();
amx::ANET_COMMAND cmd;
cmd.MC = 0x000a;
cmd.device1 = channel;
cmd.port1 = 0;
cmd.system = system;
cmd.data.message_value.system = system;
cmd.data.message_value.value = 9; // System volume
cmd.data.message_value.content.integer = vol;
cmd.data.message_value.device = channel;
cmd.data.message_value.port = 0; // Must be the address port of button
cmd.data.message_value.type = 0x20; // Unsigned int
gPageManager->doCommand(cmd);
}
}
else if (isSystem && ch == SYSTEM_ITEM_VOLUMEMUTE) // System mute
{
if (pressed)
{
TConfig::setTemporary(true);
bool mute = TConfig::getMuteState();
if (mute)
mActInstance = instance = 0;
else
mActInstance = instance = 1;
TConfig::setMuteState(!mute);
if (gPageManager && gPageManager->getCallMuteSound())
gPageManager->getCallMuteSound()(!mute);
mChanged = true;
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_BTSAVESETTINGS) // System button OK: Save settings
{
if (pressed)
{
mActInstance = instance = 1;
TConfig::setTemporary(true);
TConfig::saveSettings();
drawButton(mActInstance, true);
if (gPageManager && gPageManager->getDisplayMessage())
gPageManager->getDisplayMessage()("Settings were saved!", "Info");
else
MSG_INFO("Settings were saved.");
}
else
{
mActInstance = instance = 0;
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_BTCANCELSETTINGS) // System button Cancel: Cancel settings changes
{
if (pressed)
{
mActInstance = instance = 1;
TConfig::reset();
drawButton(mActInstance, true);
}
else
{
mActInstance = instance = 0;
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SIPENABLE) // SIP: enabled/disabled
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getSIPstatus();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::setSIPstatus(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGINFO) // Debug info
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_INFO) ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_INFO) : (ll |= HLOG_INFO));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGWARNING) // Debug warning
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_WARNING) ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_WARNING) : (ll |= HLOG_WARNING));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGERROR) // Debug error
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_ERROR) ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_ERROR) : (ll |= HLOG_ERROR));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGTRACE) // Debug trace
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_TRACE) ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_TRACE) : (ll |= HLOG_TRACE));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGDEBUG) // Debug debug
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_DEBUG) ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_DEBUG) : (ll |= HLOG_DEBUG));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROTOCOL) // Debug protocol
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_PROTOCOL) == HLOG_PROTOCOL ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_PROTOCOL) : (ll |= HLOG_PROTOCOL));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
if (gPageManager)
gPageManager->updateActualPage();
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGALL) // Debug all
{
if (pressed)
{
TConfig::setTemporary(true);
uint ll = TConfig::getLogLevelBits();
bool st = (ll & HLOG_ALL) == HLOG_ALL ? true : false;
mActInstance = instance = (st ? 0 : 1);
ll = (st ? (ll &= RLOG_ALL) : (ll |= HLOG_ALL));
mChanged = true;
TConfig::saveLogLevel(ll);
drawButton(mActInstance, true);
if (gPageManager)
gPageManager->updateActualPage();
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGPROFILE) // Log profiling
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getProfiling();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveProfiling(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_DEBUGLONG) // Log long format
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::isLongFormat();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveFormat(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_LOGRESET) // Log reset path
{
if (pressed)
{
char *HOME = getenv("HOME");
string logFile = TConfig::getLogFile();
if (HOME)
{
logFile = HOME;
logFile += "/tpanel/tpanel.log";
}
ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
TConfig::setTemporary(true);
TConfig::saveLogFile(logFile);
MSG_DEBUG("Setting text \"" << logFile << "\" to button " << handleToString(handle));
if (gPageManager)
gPageManager->setTextToButton(handle, logFile, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_LOGFILEOPEN) // Log file dialog
{
if (pressed && gPageManager && gPageManager->getFileDialogFunction())
{
TConfig::setTemporary(true);
ulong handle = (SYSTEM_PAGE_LOGGING << 16) | SYSTEM_PAGE_LOG_TXLOGFILE;
string currFile = TConfig::getLogFile();
gPageManager->getFileDialogFunction()(handle, currFile, "*.log *.txt", "log");
}
}
else if (isSystem && ch == SYSTEM_ITEM_FTPDOWNLOAD) // FTP download surface button
{
if (pressed)
{
TConfig::setTemporary(false);
string surfaceOld = TConfig::getFtpSurface();
TConfig::setTemporary(true);
string surfaceNew = TConfig::getFtpSurface();
MSG_DEBUG("Surface difference: Old: " << surfaceOld << ", New: " << surfaceNew);
if (gPageManager && gPageManager->getDownloadSurface())
{
size_t size = gPageManager->getFtpSurfaceSize(surfaceNew);
gPageManager->getDownloadSurface()(surfaceNew, size);
}
}
}
else if (isSystem && ch == SYSTEM_ITEM_FTPPASSIVE) // FTP passive mode
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getFtpPassive();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveFtpPassive(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYSYSSOUND) // Play system sound
{
if (pressed)
{
TConfig::setTemporary(true);
string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSystemSound();
if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
gPageManager->getCallPlaySound()(sound);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYBEEP) // Play single beep
{
if (pressed)
{
TConfig::setTemporary(true);
string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getSingleBeepSound();
if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
gPageManager->getCallPlaySound()(sound);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYDBEEP) // Play double beep
{
if (pressed)
{
TConfig::setTemporary(true);
string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/" + TConfig::getDoubleBeepSound();
if (!sound.empty() && gPageManager && gPageManager->getCallPlaySound())
gPageManager->getCallPlaySound()(sound);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SOUNDPLAYTESTSOUND) // Play test sound
{
if (pressed)
{
TConfig::setTemporary(true);
string sound = TConfig::getProjectPath() + "/__system/graphics/sounds/audioTest.wav";
if (gPageManager && gPageManager->getCallPlaySound())
gPageManager->getCallPlaySound()(sound);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SIPIPV4) // SIP: IPv4
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getSIPnetworkIPv4();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::setSIPnetworkIPv4(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SIPIPV6) // SIP: IPv6
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getSIPnetworkIPv6();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::setSIPnetworkIPv6(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_SIPIPHONE) // SIP: internal phone
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getSIPiphone();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::setSIPiphone(!st);
drawButton(mActInstance, true);
}
}
#ifdef __ANDROID__
else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT) // Scale to fit
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getScale();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveScale(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER) // Show banner (disabled)
{
if (sr[0].oo < 0)
{
sr[0].oo = 128;
mChanged = true;
mActInstance = 0;
drawButton(mActInstance, true);
}
}
#else
else if (isSystem && ch == SYSTEM_ITEM_VIEWSCALEFIT) // Scale to fit (disabled)
{
if (sr[0].oo < 0)
{
sr[0].oo = 128;
mChanged = true;
mActInstance = 0;
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_VIEWBANNER) // Show banner
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::showBanner();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveBanner(st);
drawButton(mActInstance, true);
}
}
#endif
else if (isSystem && ch == SYSTEM_ITEM_VIEWNOTOOLBAR) // Suppress toolbar
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getToolbarSuppress();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveToolbarSuppress(!st);
drawButton(mActInstance, true);
}
}
else if (isSystem && ch == SYSTEM_ITEM_VIEWTOOLBAR) // Force toolbar
{
if (pressed)
{
TConfig::setTemporary(true);
if (TConfig::getToolbarSuppress())
{
if (sr[0].oo < 0)
{
sr[0].oo = 128;
mChanged = true;
mActInstance = 0;
drawButton(mActInstance, true);
}
}
else
{
if (sr[0].oo >= 0)
sr[0].oo = -1;
bool st = TConfig::getToolbarForce();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::saveToolbarForce(!st);
drawButton(mActInstance, true);
}
}
}
else if (isSystem && ch == SYSTEM_ITEM_VIEWROTATE) // Lock rotation
{
if (pressed)
{
TConfig::setTemporary(true);
bool st = TConfig::getRotationFixed();
mActInstance = instance = (st ? 0 : 1);
mChanged = true;
TConfig::setRotationFixed(!st);
drawButton(mActInstance, true);
}
}
else if (fb == FB_MOMENTARY)
{
if (pressed)
instance = 1;
else
instance = 0;
MSG_DEBUG("Flavor FB_MOMENTARY, instance=" << instance);
mActInstance = instance;
mChanged = true;
if (pushFunc.empty() || (!pushFunc.empty() && instance == 0))
drawButton(instance);
// If there is nothing in "hs", then it depends on the pixel of the
// layer. Only if the pixel the coordinates point to are not
// transparent, the button takes the click.
if (hs.empty() && isPixelTransparent(sx, sy))
return false;
// Play sound, if one is defined
if (gPageManager)
{
if (pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
else if (!pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
}
if (pushFunc.empty()) // Don't draw the button if it has a push function defined
showLastButton();
else
mActInstance = 0;
}
else if (fb == FB_CHANNEL || fb == FB_NONE)
{
if (pressed)
instance = 1;
else
instance = 0;
MSG_DEBUG("Flavor FB_CHANNEL, instance=" << instance);
// If there is nothing in "hs", then it depends on the pixel of the
// layer. Only if the pixel the coordinates point to are not
// transparent, the button takes the click.
if (hs.empty() && isPixelTransparent(sx, sy))
return false;
}
else if (fb == FB_INV_CHANNEL)
{
if (pressed)
instance = 0;
else
instance = 1;
MSG_DEBUG("Flavor FB_INV_CHANNEL, instance=" << instance);
// If there is nothing in "hs", then it depends on the pixel of the
// layer. Only if the pixel the coordinates point to are not
// transparent, the button takes the click.
if (hs.empty() && isPixelTransparent(sx, sy))
return false;
// Play sound, if one is defined
if (gPageManager)
{
if (pressed && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[0].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
else if (!pressed && gPageManager->havePlaySound() && !sr[0].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[0].sd);
}
}
else if (fb == FB_ALWAYS_ON)
{
int oldInst = mActInstance;
instance = 1;
mActInstance = 1;
MSG_DEBUG("Flavor FB_ALWAYS_ON, instance=" << instance);
if (oldInst != mActInstance) // This should never become true!
{
mChanged = true;
drawButton(instance, false);
}
// If there is nothing in "hs", then it depends on the pixel of the
// layer. Only if the pixel the coordinates point to are not
// transparent, the button takes the click.
if (hs.empty() && isPixelTransparent(sx, sy))
return false;
// Play sound, if one is defined
if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[1].sd.empty() && strCaseCompare(sr[1].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[1].sd);
}
if ((cp && ch) || !op.empty())
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
{
if (instance)
scmd.MC = 0x0084;
else
scmd.MC = 0x0085;
}
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
MSG_DEBUG("Button " << bi << ", " << na << " with handle " << handleToString(mHandle));
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") << ")");
if (gAmxNet)
{
if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
gAmxNet->sendCommand(scmd);
}
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
// If this button triggers a bargraph, we handle it here.
if (pressed && !vt.empty() && lp && lv && gPageManager)
{
TButton *bt = gPageManager->findBargraph(lp, lv, getParent());
if (bt)
{
int level = bt->getLevelValue();
if (vt == "rel") // relative
{
if (rv > 0)
{
mThreadRunMove = true;
level += va;
int btRh = bt->getRangeHigh();
int btRl = bt->getRangeLow();
if (level < btRl)
level = btRl;
else if (level > btRh)
level = btRh;
for (int i = 0; i < rv; ++i)
{
if (!mThreadRunMove || level > btRh || level < btRl)
break;
gPageManager->sendInternalLevel(lp, lv, level);
if (buttonStates && level != lastSendLevelX)
{
gPageManager->sendLevel(lp, lv, level);
buttonStates->setLastSendLevelX(level);
lastSendLevelX = level;
}
level += va;
}
mThreadRunMove = false;
}
else
{
level += va;
if (level < bt->getRangeLow())
level = bt->getRangeLow();
else if (level > bt->getRangeHigh())
level = bt->getRangeHigh();
gPageManager->sendInternalLevel(lp, lv, level);
if (buttonStates && lastSendLevelX != (level))
{
gPageManager->sendLevel(lp, lv, level);
buttonStates->setLastSendLevelX(level);
lastSendLevelX = level;
}
}
}
else // absolute
{
gPageManager->sendInternalLevel(lp, lv, va);
if (buttonStates && lastSendLevelX != va)
{
gPageManager->sendLevel(lp, lv, va);
buttonStates->setLastSendLevelX(va);
lastSendLevelX = va;
}
}
}
else
MSG_DEBUG("Found no bargraph with lp=" << lp << ", lv=" << lv);
}
else if (!pressed && !vt.empty() && lp && lv)
{
mThreadRunMove = false;
}
}
else if (type == MULTISTATE_GENERAL)
{
// Play sound, if one is defined
if (pressed && gPageManager && gPageManager->havePlaySound() && !sr[mActInstance].sd.empty() && strCaseCompare(sr[mActInstance].sd, "None") != 0)
gPageManager->getCallPlaySound()(TConfig::getProjectPath() + "/sounds/" + sr[mActInstance].sd);
if ((cp && ch) || !op.empty())
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
{
if (pressed || fb == FB_ALWAYS_ON)
scmd.MC = 0x0084;
else
scmd.MC = 0x0085;
}
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
MSG_DEBUG("Button " << bi << ", " << na << " with handle " << handleToString(mHandle));
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") << ")");
if (gAmxNet)
{
if (scmd.MC != 0x008b || (pressed && scmd.MC == 0x008b))
gAmxNet->sendCommand(scmd);
}
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
}
else if (type == BARGRAPH && (lf == "active" || lf == "center"))
{
// Find the click position
int level = 0;
if (!pressed)
mRunBargraphMove = false;
if (!pressed && lf == "center")
level = (rh - rl) / 2;
else
{
if (dr.compare("horizontal") == 0)
{
level = x;
level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(wt) * static_cast<double>(level));
}
else
{
level = ht - y;
level = static_cast<int>(static_cast<double>(rh - rl) / static_cast<double>(ht) * static_cast<double>(level));
}
}
if (isSystem)
{
// Draw the bargraph
if (!drawBargraph(mActInstance, level, visible))
return false;
// Handle click
if (lv == 9) // System volume button
{
if (!pressed)
{
TConfig::saveSystemVolume(level);
TConfig::saveSettings();
}
}
}
else if ((pressed && cp && ch) || (pressed && !op.empty()))
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0084; // push button
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
if (!isSystem)
{
int distance = (lastLevel > level ? (lastLevel - level) : (level - lastLevel));
bool directionUp = (lastLevel > level);
if (pressed && distance > 0)
runBargraphMove(distance, directionUp);
else if (!pressed)
{
if (lf == "active")
level = lastLevel;
else if (level != lastLevel)
drawBargraph(mActInstance, level);
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
gPageManager->sendLevel(lp, lv, (ri ? ((rh - rl) - level) : level));
lastSendLevelX = level;
if (buttonStates)
buttonStates->setLastSendLevelX(level);
}
}
if ((!pressed && cp && ch) || (!pressed && !op.empty()))
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0085; // release button
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
}
}
else if (type == BARGRAPH && (lf == "drag" || lf == "dragCenter") && pressed)
{
mBarStartLevel = lastLevel;
int level;
if (dr.compare("horizontal") == 0)
{
level = x;
level = (int)((double)(rh - rl) / (double)wt * (double)level);
}
else
{
level = ht - y;
level = (int)((double)(rh - rl) / (double)ht * (double)level);
}
mBarThreshold = mBarStartLevel - level;
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0084; // push button
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
else if (type == BARGRAPH && (lf == "drag" || lf == "dragCenter") && !pressed)
{
if (lf == "dragCenter")
{
int level = (rh - rl) / 2;
mBarStartLevel = level;
// Draw the bargraph
if (!drawBargraph(mActInstance, level, visible))
return false;
// Send the level
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri ? ((rh - rl) - level) : level);
scmd.MC = 0x008a;
if (gAmxNet)
{
if (lastSendLevelX != level)
gAmxNet->sendCommand(scmd);
lastSendLevelX = level;
if (buttonStates)
buttonStates->setLastSendLevelX(level);
}
}
}
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0085; // release button
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
else if (type == TEXT_INPUT)
{
MSG_DEBUG("Text area detected. Switching on keyboard");
// Drawing background graphic (visible part of area)
drawTextArea(mActInstance);
}
else if (type == JOYSTICK && !lf.empty())
{
if (!pressed && (lf == "center" || lf == "dragCenter"))
sx = sy = (rh - rl) / 2;
if (pressed && ((cp && ch) || !op.empty()))
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0084;
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
if (!drawJoystick(sx, sy))
return false;
// Send the levels
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri ? ((rh - rl) - sx) : sx);
scmd.MC = 0x008a;
if (gAmxNet)
{
if (lastSendLevelX != scmd.value)
gAmxNet->sendCommand(scmd);
lastJoyX = sx;
lastSendLevelX = scmd.value;
if (buttonStates)
buttonStates->setLastSendLevelX(lastSendLevelX);
}
scmd.channel = lv + 1;
scmd.level = lv + 1;
scmd.value = (ji ? ((rh - rl) - sy) : sy);
if (gAmxNet)
{
if (lastSendLevelY != scmd.value)
gAmxNet->sendCommand(scmd);
lastJoyY = sy;
lastSendLevelY = scmd.value;
if (buttonStates)
buttonStates->setLastSendLevelY(lastSendLevelY);
}
}
if (!pressed && ((cp && ch) || !op.empty()))
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
if (op.empty())
scmd.MC = 0x0085;
else
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet)
gAmxNet->sendCommand(scmd);
else
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
}
else if (type == JOYSTICK && lf.empty())
{
if ((cp && ch) || !op.empty())
{
scmd.device = TConfig::getChannel();
scmd.port = cp;
scmd.channel = ch;
scmd.MC = 0;
if (op.empty())
scmd.MC = (pressed ? 0x0084 : 0x0085);
else if (pressed)
{
scmd.MC = 0x008b;
scmd.msg = op;
}
if (gAmxNet && scmd.MC != 0)
gAmxNet->sendCommand(scmd);
else if (!gAmxNet)
MSG_WARNING("Missing global class TAmxNet. Can't send a message!");
}
}
/* FIXME: Move the following to class TPageManager!
* To do that, the preconditions are to be implemented. It must be
* possible to find the button and get access to the credentials
* of it.
*/
if (!pushFunc.empty() && pressed)
{
MSG_DEBUG("Executing a push function ...");
vector<PUSH_FUNC_T>::iterator iter;
for (iter = pushFunc.begin(); iter != pushFunc.end(); ++iter)
{
if (fb == FB_MOMENTARY || fb == FB_NONE)
mActInstance = 0;
else if (fb == FB_ALWAYS_ON || fb == FB_INV_CHANNEL)
mActInstance = 1;
if (!TTPInit::isTP5() || iter->action == BT_ACTION_PGFLIP)
{
MSG_DEBUG("Testing for function \"" << iter->pfType << "\"");
if (strCaseCompare(iter->pfType, "SSHOW") == 0) // show popup
{
if (gPageManager)
gPageManager->showSubPage(iter->pfName);
}
else if (strCaseCompare(iter->pfType, "SHIDE") == 0) // hide popup
{
if (gPageManager)
gPageManager->hideSubPage(iter->pfName);
}
else if (strCaseCompare(iter->pfType, "SCGROUP") == 0) // hide group
{
if (gPageManager)
gPageManager->closeGroup(iter->pfName);
}
else if (strCaseCompare(iter->pfType, "SCPAGE") == 0) // flip to page
{
if (gPageManager && !iter->pfName.empty())
gPageManager->setPage(iter->pfName);
}
else if (strCaseCompare(iter->pfType, "STAN") == 0) // Flip to standard page
{
if (gPageManager)
{
if (!iter->pfName.empty())
gPageManager->setPage(iter->pfName);
else
{
TPage *page = gPageManager->getActualPage();
if (!page)
{
MSG_DEBUG("Internal error: No actual page found!");
return false;
}
TSettings *settings = gPageManager->getSettings();
if (settings && settings->getPowerUpPage().compare(page->getName()) != 0)
gPageManager->setPage(settings->getPowerUpPage());
}
}
}
else if (strCaseCompare(iter->pfType, "FORGET") == 0) // Flip to page and forget
{
if (gPageManager && !iter->pfName.empty())
gPageManager->setPage(iter->pfName, true);
}
else if (strCaseCompare(iter->pfType, "PREV") == 0) // Flip to previous page
{
if (gPageManager)
{
int old = gPageManager->getPreviousPageNumber();
if (old > 0)
gPageManager->setPage(old);
}
}
else if (strCaseCompare(iter->pfType, "STOGGLE") == 0) // Toggle popup state
{
if (!iter->pfName.empty() && gPageManager)
{
TSubPage *page = gPageManager->getSubPage(iter->pfName);
if (!page) // Is the page not in cache?
{ // No, then load it
gPageManager->showSubPage(iter->pfName);
return true;
}
if (page->isVisible())
gPageManager->hideSubPage(iter->pfName);
else
gPageManager->showSubPage(iter->pfName);
}
}
else if (strCaseCompare(iter->pfType, "SCPANEL") == 0) // Hide all popups
{
if (gPageManager)
{
TSubPage *page = gPageManager->getFirstSubPage();
while (page)
{
page->drop();
page = gPageManager->getNextSubPage();
}
}
}
else
{
MSG_WARNING("Unknown page flip command " << iter->pfType);
}
}
else if (iter->action == BT_ACTION_LAUNCH)
{
#ifdef __ANDROID__
MSG_DEBUG("Launching the external program " << iter->pfName << "...");
#else
MSG_DEBUG("Launching the external program " << iter->pfName << "...");
TLauncher::launch(iter->pfName);
#endif // __ANDROID__
}
}
}
if (!cm.empty() && co == 0 && pressed) // Feed command to ourself?
{ // Yes, then feed it into command queue.
MSG_DEBUG("Button has a self feed command");
int channel = TConfig::getChannel();
int system = TConfig::getSystem();
if (gPageManager)
{
amx::ANET_COMMAND cmd;
cmd.intern = true;
cmd.MC = 0x000c;
cmd.device1 = channel;
cmd.port1 = 1;
cmd.system = system;
cmd.data.message_string.device = channel;
cmd.data.message_string.port = 1; // Must be 1
cmd.data.message_string.system = system;
cmd.data.message_string.type = 1; // 8 bit char string
vector<string>::iterator iter;
for (iter = cm.begin(); iter != cm.end(); ++iter)
{
cmd.data.message_string.length = iter->length();
memset(&cmd.data.message_string.content, 0, sizeof(cmd.data.message_string.content));
strncpy((char *)&cmd.data.message_string.content, iter->c_str(), sizeof(cmd.data.message_string.content)-1);
MSG_DEBUG("Executing system command: " << *iter);
gPageManager->doCommand(cmd);
}
}
}
else if (!cm.empty() && pressed)
{
MSG_DEBUG("Button sends a command on port " << co);
if (gPageManager)
{
vector<string>::iterator iter;
for (iter = cm.begin(); iter != cm.end(); ++iter)
gPageManager->sendCommandString(co, *iter);
}
}
return true;
}
/**
* Based on the pixels in the \a basePix, the function decides whether to return
* the value of \a col1 or \a col2. A red pixel returns the color \a col1 and
* a green pixel returns the color \a col2. If there is no red and no green
* pixel, a transparent pixel is returned.
*
* @param basePix
* This is a pixel from a mask containing red and/or green pixels.
*
* @param maskPix
* This is a pixel from a mask containing more or less tranparent pixels. If
* the alpha channel of \a basePix is 0 (transparent) this pixel is returned.
*
* @param col1
* The first color.
*
* @param col2
* The second color.
*
* @return
* An array containing the color for one pixel.
*/
SkColor TButton::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
{
uint alpha = SkColorGetA(basePix);
uint green = SkColorGetG(basePix);
uint red = 0;
if (isBigEndian())
red = SkColorGetB(basePix);
else
red = SkColorGetR(basePix);
if (alpha == 0)
return maskPix;
if (red && green)
{
if (red < green)
return col2;
return col1;
}
if (red)
return col1;
if (green)
return col2;
return SK_ColorTRANSPARENT; // transparent pixel
}
TEXT_EFFECT TButton::textEffect(const std::string& effect)
{
DECL_TRACER("TButton::textEffect(const std::string& effect)");
if (effect == "Outline-S")
return EFFECT_OUTLINE_S;
else if (effect == "Outline-M")
return EFFECT_OUTLINE_M;
else if (effect == "Outline-L")
return EFFECT_OUTLINE_L;
else if (effect == "Outline-X")
return EFFECT_OUTLINE_X;
else if (effect == "Glow-S")
return EFFECT_GLOW_S;
else if (effect == "Glow-M")
return EFFECT_GLOW_M;
else if (effect == "Glow-L")
return EFFECT_GLOW_L;
else if (effect == "Glow-X")
return EFFECT_GLOW_X;
else if (effect == "Soft Drop Shadow 1")
return EFFECT_SOFT_DROP_SHADOW_1;
else if (effect == "Soft Drop Shadow 2")
return EFFECT_SOFT_DROP_SHADOW_2;
else if (effect == "Soft Drop Shadow 3")
return EFFECT_SOFT_DROP_SHADOW_3;
else if (effect == "Soft Drop Shadow 4")
return EFFECT_SOFT_DROP_SHADOW_4;
else if (effect == "Soft Drop Shadow 5")
return EFFECT_SOFT_DROP_SHADOW_5;
else if (effect == "Soft Drop Shadow 6")
return EFFECT_SOFT_DROP_SHADOW_6;
else if (effect == "Soft Drop Shadow 7")
return EFFECT_SOFT_DROP_SHADOW_7;
else if (effect == "Soft Drop Shadow 8")
return EFFECT_SOFT_DROP_SHADOW_8;
else if (effect == "Medium Drop Shadow 1")
return EFFECT_MEDIUM_DROP_SHADOW_1;
else if (effect == "Medium Drop Shadow 2")
return EFFECT_MEDIUM_DROP_SHADOW_2;
else if (effect == "Medium Drop Shadow 3")
return EFFECT_MEDIUM_DROP_SHADOW_3;
else if (effect == "Medium Drop Shadow 4")
return EFFECT_MEDIUM_DROP_SHADOW_4;
else if (effect == "Medium Drop Shadow 5")
return EFFECT_MEDIUM_DROP_SHADOW_5;
else if (effect == "Medium Drop Shadow 6")
return EFFECT_MEDIUM_DROP_SHADOW_6;
else if (effect == "Medium Drop Shadow 7")
return EFFECT_MEDIUM_DROP_SHADOW_7;
else if (effect == "Medium Drop Shadow 8")
return EFFECT_MEDIUM_DROP_SHADOW_8;
else if (effect == "Hard Drop Shadow 1")
return EFFECT_HARD_DROP_SHADOW_1;
else if (effect == "Hard Drop Shadow 2")
return EFFECT_HARD_DROP_SHADOW_2;
else if (effect == "Hard Drop Shadow 3")
return EFFECT_HARD_DROP_SHADOW_3;
else if (effect == "Hard Drop Shadow 4")
return EFFECT_HARD_DROP_SHADOW_4;
else if (effect == "Hard Drop Shadow 5")
return EFFECT_HARD_DROP_SHADOW_5;
else if (effect == "Hard Drop Shadow 6")
return EFFECT_HARD_DROP_SHADOW_6;
else if (effect == "Hard Drop Shadow 7")
return EFFECT_HARD_DROP_SHADOW_7;
else if (effect == "Hard Drop Shadow 8")
return EFFECT_HARD_DROP_SHADOW_8;
else if (effect == "Soft Drop Shadow 1 with outline")
return EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 2 with outline")
return EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 3 with outline")
return EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 4 with outline")
return EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 5 with outline")
return EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 6 with outline")
return EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 7 with outline")
return EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE;
else if (effect == "Soft Drop Shadow 8 with outline")
return EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 1 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 2 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 3 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 4 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 5 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 6 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 7 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE;
else if (effect == "Medium Drop Shadow 8 with outline")
return EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 1 with outline")
return EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 2 with outline")
return EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 3 with outline")
return EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 4 with outline")
return EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 5 with outline")
return EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 6 with outline")
return EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 7 with outline")
return EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE;
else if (effect == "Hard Drop Shadow 8 with outline")
return EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE;
return EFFECT_NONE;
}
bool TButton::isSystemButton()
{
DECL_TRACER("TButton::isSystemButton()");
if (type == MULTISTATE_BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
return true;
else if (type == BARGRAPH && lp == 0 && TSystem::isSystemButton(lv))
return true;
else if (type == LISTBOX && ap == 0 && ad > 0 && ti >= SYSTEM_PAGE_START)
return true;
else if (ap == 0 && TSystem::isSystemButton(ad))
return true;
else if (cp == 0 && TSystem::isSystemButton(ch))
return true;
return false;
}
THR_REFRESH_t *TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)
{
DECL_TRACER("TButton::_addResource(TImageRefresh* refr, ulong handle, ulong parent, int bi)");
THR_REFRESH_t *p = mThrRefresh;
THR_REFRESH_t *r, *last = p;
if (!refr || !handle || !parent || bi <= 0)
{
MSG_ERROR("Invalid parameter!");
return nullptr;
}
r = new THR_REFRESH_t;
r->mImageRefresh = refr;
r->handle = handle;
r->parent = parent;
r->bi = bi;
r->next = nullptr;
// If the chain is empty, add the new item;
if (!mThrRefresh)
mThrRefresh = r;
else // Find the end and append the item
{
while (p)
{
last = p;
if (p->handle == handle && p->parent == parent && p->bi == bi)
{
MSG_WARNING("Duplicate button found! Didn't add it again.");
delete r;
return p;
}
p = p->next;
}
last->next = r;
}
MSG_DEBUG("New dynamic button added.");
return r;
}
THR_REFRESH_t *TButton::_findResource(ulong handle, ulong parent, int bi)
{
DECL_TRACER("TButton::_findResource(ulong handle, ulong parent, int bi)");
THR_REFRESH_t *p = mThrRefresh;
while (p)
{
if (p->handle == handle && p->parent == parent && p->bi == bi)
return p;
p = p->next;
}
return nullptr;
}
void TButton::addToBitmapCache(BITMAP_CACHE& bc)
{
DECL_TRACER("TButton::addToBitmapCache(BITMAP_CACHE& bc)");
if (nBitmapCache.size() == 0)
{
nBitmapCache.push_back(bc);
return;
}
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->handle == bc.handle && iter->parent == bc.parent && iter->bi == bc.bi)
{
nBitmapCache.erase(iter);
nBitmapCache.push_back(bc);
return;
}
}
nBitmapCache.push_back(bc);
}
BITMAP_CACHE& TButton::getBCentryByHandle(ulong handle, ulong parent)
{
DECL_TRACER("TButton::getBCentryByHandle(ulong handle, ulong parent)");
if (nBitmapCache.size() == 0)
return mBCDummy;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->handle == handle && iter->parent == parent)
return *iter;
}
return mBCDummy;
}
BITMAP_CACHE& TButton::getBCentryByBI(int bIdx)
{
DECL_TRACER("TButton::getBCentryByBI(int bIdx)");
if (nBitmapCache.size() == 0)
return mBCDummy;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->bi == bIdx)
return *iter;
}
return mBCDummy;
}
void TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)
{
DECL_TRACER("TButton::removeBCentry(std::vector<BITMAP_CACHE>::iterator *elem)");
if (nBitmapCache.size() == 0 || !elem)
return;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter == *elem)
{
nBitmapCache.erase(iter);
return;
}
}
}
void TButton::setReady(ulong handle)
{
DECL_TRACER("TButton::setReady(ulong handle)");
if (nBitmapCache.size() == 0)
return;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->handle == handle)
{
iter->ready = true;
return;
}
}
}
void TButton::setInvalid(ulong handle)
{
DECL_TRACER("TButton::setInvalid(ulong handle)");
if (nBitmapCache.size() == 0)
return;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->handle == handle)
{
nBitmapCache.erase(iter);
return;
}
}
}
void TButton::setBCBitmap(ulong handle, SkBitmap& bm)
{
DECL_TRACER("TButton::setBCBitmap(ulong handle, SkBitmap& bm)");
if (nBitmapCache.size() == 0)
return;
vector<BITMAP_CACHE>::iterator iter;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->handle == handle)
{
iter->bitmap = bm;
return;
}
}
}
void TButton::showBitmapCache()
{
DECL_TRACER("TButton::showBitmapCache()");
vector<BITMAP_CACHE>::iterator iter;
bool found;
while (nBitmapCache.size() > 0)
{
found = false;
for (iter = nBitmapCache.begin(); iter != nBitmapCache.end(); ++iter)
{
if (iter->ready)
{
if (_displayButton)
{
TBitmap image((unsigned char *)iter->bitmap.getPixels(), iter->bitmap.info().width(), iter->bitmap.info().height());
_displayButton(iter->handle, iter->parent, image, iter->width, iter->height, iter->left, iter->top, isPassThrough(), sr[mActInstance].md, sr[mActInstance].mr);
if (sr[mActInstance].md > 0 && sr[mActInstance].mr > 0)
{
if (gPageManager && gPageManager->getSetMarqueeText())
gPageManager->getSetMarqueeText()(this);
}
mChanged = false;
}
nBitmapCache.erase(iter);
found = true;
break;
}
}
if (!found)
break;
}
}
uint32_t TButton::pixelMix(uint32_t s, uint32_t d, uint32_t a, PMIX mix)
{
DECL_TRACER("TButton::pixelMultiply(uint32_t s, uint32_t d)");
uint32_t r = 0;
switch(mix)
{
case PMIX_SRC: r = s; break; // SRC
case PMIX_DST: r = d; break; // DST
case PMIX_MULTIPLY: r = s * (255 - (d * a)) + d * (255 - (s * a)) + s * d; break; // Multiply
case PMIX_PLUS: r = std::min(s + d, (uint32_t)255); break; // plus
case PMIX_XOR: r = s * (255 - (d * a)) + d * (255 - (s * a)); break; // XOr
case PMIX_DSTTOP: r = d * (s * a) + s * (255 - (d * a)); break; // DstATop
case PMIX_SRCTOP: r = s * (d * a) + d * (255 - (s * a)); break; // SrcATop
case PMIX_SRCOVER: r = s + (255 - (s * a)) * d; break; // SrcOver
case PMIX_SCREEN: r = s + d - s * d; break; // Screen
}
return r & 0x00ff;
}
bool TButton::isPassThrough()
{
DECL_TRACER("TButton::isPassThrough()");
if (hs.empty())
return false;
if (strCaseCompare(hs, "passThru") == 0)
return true;
return false;
}
/**
* @brief Flip the red and blue color level
* Swaps the red and blue color level. This is sometimes necessary to preserve
* the correct color.
* This method changes the content of the parameter \b color to the swapped
* value!
*
* @param color The color to swap.
* @return Returns the the color where red and blue level was swapped.
*/
SkColor& TButton::flipColorLevelsRB(SkColor& color)
{
DECL_TRACER("TButton::flipColorLevelsRB(SkColor& color)");
SkColor red = SkColorGetR(color);
SkColor green = SkColorGetG(color);
SkColor blue = SkColorGetB(color);
SkColor alpha = SkColorGetA(color);
color = SkColorSetARGB(alpha, blue, green, red);
return color;
}
void TButton::runBargraphMove(int distance, bool moveUp)
{
DECL_TRACER("TButton::runBargraphMove(int distance, bool moveUp)");
if (mThreadRunMove)
return;
mRunBargraphMove = true;
try
{
mThrSlider = thread([=] { threadBargraphMove(distance, moveUp); });
mThrSlider.detach();
}
catch (std::exception& e)
{
MSG_ERROR("Error starting thread: " << e.what());
mRunBargraphMove = false;
mThreadRunMove = false;
}
}
void TButton::threadBargraphMove(int distance, bool moveUp)
{
DECL_TRACER("TButton::threadBargraphMove(int distance, bool moveUp)");
if (mThreadRunMove)
return;
mThreadRunMove = true;
TButtonStates *buttonStates = getButtonState();
int lLevel = 0;
int lastSendLevelX = 0;
int lastSendLevelY = 0;
if (buttonStates)
{
lLevel = buttonStates->getLastLevel();
lastSendLevelX = buttonStates->getLastSendLevelX();
lastSendLevelY = buttonStates->getLastSendLevelY();
}
int ispeed = (moveUp ? lu : ld);
if (ispeed <= 0)
ispeed = 1;
ispeed *= 100; // Time is 1/10 of seconds but we need milliseconds
double speed = static_cast<double>(ispeed);
double total = static_cast<double>(distance) * speed;
double step = 1.0;
double pos = 0.0;
double lastLevel = static_cast<double>(lLevel);
double posLevel = lastLevel;
if (step <= 0.0)
step = 1.0;
std::chrono::milliseconds ms = std::chrono::milliseconds(static_cast<long>(total));
std::chrono::milliseconds msStep = std::chrono::milliseconds(static_cast<long>(step));
MSG_DEBUG("step: " << step << ", total time (ms): " << total << ", distance: " << distance << ", speed: " << speed);
for (std::chrono::milliseconds mi = std::chrono::milliseconds(0); mi < ms; mi += msStep)
{
if (!mRunBargraphMove)
break;
lastLevel = (moveUp ? (posLevel - pos) : (posLevel + pos));
if (static_cast<int>(lastLevel) != lLevel)
{
int level = static_cast<int>(lastLevel);
if (!drawBargraph(mActInstance, level))
break;
if (lp && lv && gPageManager && gPageManager->getLevelSendState())
{
amx::ANET_SEND scmd;
scmd.device = TConfig::getChannel();
scmd.port = lp;
scmd.channel = lv;
scmd.level = lv;
scmd.value = (ri ? ((rh - rl) - level) : level);
scmd.MC = 0x008a;
if (gAmxNet)
{
if (lastSendLevelX != level)
gAmxNet->sendCommand(scmd);
lastSendLevelX = level;
if (buttonStates)
buttonStates->setLastSendLevelX(level);
}
}
}
if (pos >= static_cast<double>(distance))
break;
pos += step;
std::this_thread::sleep_for(std::chrono::milliseconds(msStep));
}
mThreadRunMove = false;
}
TButtonStates *TButton::getButtonState()
{
DECL_TRACER("TButton::getButtonState()");
if (!gPageManager)
return nullptr;
TButtonStates *s = gPageManager->getButtonState(type, mButtonID);
MSG_DEBUG("Found button ID: " << getButtonIDstr(s->getID()) << ", type: " << buttonTypeToString(s->getType()) << ", lastLevel: " << s->getLastLevel() << ", lastJoyX: " << s->getLastJoyX() << ", lasJoyY: " << s->getLastJoyY());
return s;
}
bool TButton::isButtonEvent(const string& token, const vector<string>& events)
{
DECL_TRACER("TButton::isButtonEvent(const string& token, const vector<string>& events)");
if (events.empty() || token.empty())
return false;
vector<string>::const_iterator iter;
for (iter = events.cbegin(); iter != events.cend(); ++iter)
{
if (*iter == token)
return true;
}
return false;
}
BUTTON_EVENT_t TButton::getButtonEvent(const string& token)
{
DECL_TRACER("TButton::getButtonEvent(const string& token)");
if (token == "ga")
return EVENT_GUESTURE_ANY;
else if (token == "gu")
return EVENT_GUESTURE_UP;
else if (token == "gd")
return EVENT_GUESTURE_DOWN;
else if (token == "gr")
return EVENT_GUESTURE_RIGHT;
else if (token == "gl")
return EVENT_GUESTURE_LEFT;
else if (token == "gt")
return EVENT_GUESTURE_DBLTAP;
else if (token == "tu")
return EVENT_GUESTURE_2FUP;
else if (token == "td")
return EVENT_GUESTURE_2FDN;
else if (token == "tr")
return EVENT_GUESTURE_2FRT;
else if (token == "tl")
return EVENT_GUESTURE_2FLT;
return EVENT_NONE;
}
int TButton::getLevelValue()
{
DECL_TRACER("TButton::getLevelValue()");
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
return 0;
}
int level = buttonStates->getLastLevel();
if (ri > 0)
level = (rh - rl) - level;
return level;
}
void TButton::setLevelValue(int level)
{
DECL_TRACER("TButton::setLevelValue(int level)");
if (level < rl || level > rh)
return;
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
return;
buttonStates->setLastLevel(level);
}
int TButton::getLevelAxisX()
{
DECL_TRACER("TButton::getLevelAxisX()");
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
return 0;
}
int level = buttonStates->getLastJoyX();
if (ri > 0)
level = (rh - rl) - level;
return level;
}
int TButton::getLevelAxisY()
{
DECL_TRACER("TButton::getLevelAxisY()");
TButtonStates *buttonStates = getButtonState();
if (!buttonStates)
{
MSG_ERROR("Button states not found!");
return 0;
}
int level = buttonStates->getLastJoyY();
if (ji > 0)
level = (rh - rl) - level;
return level;
}
string TButton::getButtonIDstr(uint32_t rid)
{
uint32_t id = (rid == 0x1fffffff ? mButtonID : rid);
std::stringstream s;
s << std::setfill('0') << std::setw(8) << std::hex << id;
return s.str();
}
bool TButton::setListSource(const string &source, const vector<string>& configs)
{
DECL_TRACER("TButton::setListSource(const string &source, const vector<string>& configs)");
TUrl url;
listSourceUser.clear();
listSourcePass.clear();
listSourceCsv = false;
listSourceHasHeader = false;
if (configs.size() > 0)
{
vector<string>::const_iterator iter;
for (iter = configs.begin(); iter != configs.end(); ++iter)
{
size_t pos;
if ((pos = iter->find("user=")) != string::npos)
listSourceUser = iter->substr(pos + 5);
else if ((pos = iter->find("pass=")) != string::npos)
listSourcePass = iter->substr(pos + 5);
else if (iter->find("csv=") != string::npos)
{
string str = *iter;
string low = toLower(str);
if (low.find("true") != string::npos || low.find("1") != string::npos)
listSourceCsv = true;
}
else if (iter->find("has_header=") != string::npos)
{
string str = *iter;
string low = toLower(str);
if (low.find("true") != string::npos || low.find("1") != string::npos)
listSourceHasHeader = true;
}
}
}
if (!url.setUrl(source)) // Dynamic source?
{
size_t idx = 0;
if (!gPrjResources)
return false;
if ((idx = gPrjResources->getResourceIndex("image")) == TPrjResources::npos)
{
MSG_ERROR("There exists no image resource!");
return false;
}
RESOURCE_T resource = gPrjResources->findResource(static_cast<int>(idx), source);
if (resource.protocol.empty())
{
MSG_WARNING("Resource " << source << " not found!");
return false;
}
listSource = resource.protocol + "://";
if (!resource.user.empty() || !listSourceUser.empty())
{
listSource += ((listSourceUser.empty() == false) ? listSourceUser : resource.user);
if ((!resource.password.empty() && !resource.encrypted) || !listSourcePass.empty())
listSource += ":" + ((listSourcePass.empty() == false) ? listSourcePass : resource.password);
listSource += "@";
}
listSource += resource.host;
if (!resource.path.empty())
listSource += "/" + resource.path;
if (!resource.file.empty())
listSource += "/" + resource.file;
return true;
}
listSource = source;
return true;
}
bool TButton::setListSourceFilter(const string& filter)
{
DECL_TRACER("TButton::setListSourceFilter(const string& filter)");
if (filter.empty())
return false;
listFilter = filter;
MSG_DEBUG("listSourceFilter: " << listFilter);
return true;
}
void TButton::setListViewColumns(int cols)
{
DECL_TRACER("TButton::setListViewColumns(int cols)");
if (cols <= 0)
return;
tc = cols;
}
void TButton::setListViewLayout(int layout)
{
DECL_TRACER("TButton::setListViewLayout(int layout)");
if (layout < 1 || layout > 6)
return;
listLayout = layout;
}
void TButton::setListViewComponent(int comp)
{
DECL_TRACER("TButton::setListViewComponent(int comp)");
if (comp < 0 || comp > 7)
return;
listComponent = comp;
}
void TButton::setListViewCellheight(int height, bool percent)
{
DECL_TRACER("TButton::setListViewCellheight(int height, bool percent)");
int minHeight = ht / tr; // Total height / number of rows
int maxHeight = (int)((double)ht / 100.0 * 95.0);
if (!percent && (height < minHeight || height > maxHeight))
return;
if (percent)
{
int h = (int)((double)ht / 100.0 * (double)height);
if (h >= minHeight && h <= maxHeight)
tj = h;
return;
}
tj = height;
}
void TButton::setListViewFilterHeight(int height, bool percent)
{
DECL_TRACER("TButton::setListViewFilterHeight(int height, bool percent)");
if (percent && (height < 5 || height > 25))
return;
if (!percent && height < 24)
return;
if (percent)
{
listViewColFilterHeight = (int)((double)ht / 100.0 * (double)height);
return;
}
else
{
int maxHeight = (int)((double)ht / 100.0 * 25.0);
if (height < maxHeight)
listViewColFilterHeight = height;
}
}
void TButton::setListViewP1(int p1)
{
DECL_TRACER("TButton::setListViewP1(int p1)");
if (p1 < 10 || p1 > 90)
return;
listViewP1 = p1;
}
void TButton::setListViewP2(int p2)
{
DECL_TRACER("TButton::setListViewP2(int p2)");
if (p2 < 10 || p2 > 90)
return;
listViewP2 = p2;
}
void TButton::listViewNavigate(const string &command, bool select)
{
DECL_TRACER("TButton::listViewNavigate(const string &command, bool select)");
string cmd = command;
string upCmd = toUpper(cmd);
if (upCmd != "T" && upCmd != "B" && upCmd != "D" && upCmd != "U" && !isNumeric(upCmd, true))
return;
// TODO: Add code to navigate a list
MSG_WARNING("ListView navigation is not supported!" << " [" << upCmd << ", " << (select ? "TRUE" : "FALSE") << "]");
}
void TButton::listViewRefresh(int interval, bool force)
{
DECL_TRACER("TButton::listViewRefresh(int interval, bool force)");
Q_UNUSED(interval);
Q_UNUSED(force);
// TODO: Add code to load list data and display / refresh them
}
void TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)
{
DECL_TRACER("TButton::listViewSortData(const vector<string> &columns, LIST_SORT order, const string &override)");
Q_UNUSED(columns);
Q_UNUSED(order);
Q_UNUSED(override);
// TODO: Insert code to sort the data in the list
}