Rev 19 | Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020, 2021 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 <exception>
#include "tresources.h"
#include "treadxml.h"
#include "tsubpage.h"
#include "tconfig.h"
#include "terror.h"
using std::string;
using std::vector;
using namespace Button;
TSubPage::TSubPage(const string& name)
: mFile(name)
{
DECL_TRACER("TSubPage::TSubPage(const string& path)");
TError::clear();
string path = makeFileName(TConfig::getProjectPath(), name);
if (isValidFile())
mFName = getFileName();
else
{
MSG_ERROR("Either the path \"" << TConfig::getProjectPath() << "\" or the file name \"" << name << "\" is invalid!");
TError::setError();
return;
}
initialize();
}
TSubPage::~TSubPage()
{
DECL_TRACER("TSubPage::~TSubPage()");
if (mSubpage.name.empty())
{
MSG_WARNING("Invalid page found!");
return;
}
MSG_DEBUG("Destroing subpage " << mSubpage.pageID << ": " << mSubpage.name);
BUTTONS_T *b = mButtons;
BUTTONS_T *next = nullptr;
while (b)
{
next = b->next;
if (b->button)
delete b->button;
delete b;
b = next;
}
mButtons = nullptr;
}
void TSubPage::initialize()
{
DECL_TRACER("TSubPage::initialize()");
if (mFName.empty())
return;
TError::clear();
TReadXML reader(mFName);
if (TError::isError())
{
MSG_DEBUG("Stopped scanning the subpage " << mFile << " due to previous errors!");
return;
}
reader.findElement("page", "type");
if (!reader.success())
{
MSG_ERROR("Element \"page\" with attribute \"type\" was not found! Invalid XML file!");
TError::setError();
return;
}
if (reader.getAttribute("type").compare("subpage") != 0)
{
MSG_ERROR("The type " << reader.getAttribute("type") << " is invalid for a subpage!");
TError::setError();
return;
}
mSubpage.popupType = reader.getAttribute("popupType");
mxml_node_t *node = reader.getFirstChild();
while (node)
{
string ename = reader.getElementName(node);
if (ename.compare("pageID") == 0)
mSubpage.pageID = reader.getIntFromNode(node);
else if (ename.compare("name") == 0)
mSubpage.name = reader.getTextFromNode(node);
else if (ename.compare("left") == 0)
mSubpage.left = reader.getIntFromNode(node);
else if (ename.compare("top") == 0)
mSubpage.top = reader.getIntFromNode(node);
else if (ename.compare("width") == 0)
mSubpage.width = reader.getIntFromNode(node);
else if (ename.compare("height") == 0)
mSubpage.height = reader.getIntFromNode(node);
else if (ename.compare("group") == 0)
mSubpage.group = reader.getTextFromNode(node);
else if (ename.compare("showEffect") == 0)
mSubpage.showEffect = (SHOWEFFECT)reader.getIntFromNode(node);
else if (ename.compare("showTime") == 0)
mSubpage.showTime = reader.getIntFromNode(node);
else if (ename.compare("hideTime") == 0)
mSubpage.hideTime = reader.getIntFromNode(node);
else if (ename.compare("hideEffect") == 0)
mSubpage.hideEffect = (SHOWEFFECT)reader.getIntFromNode(node);
else if (ename.compare("timeout") == 0)
mSubpage.timeout = reader.getIntFromNode(node);
else if (ename.compare("button") == 0) // Read a button
{
try
{
TButton *button = new TButton();
button->setPalette(mPalette);
button->setFonts(mFonts);
button->initialize(&reader, node);
button->setParentSize(mSubpage.width, mSubpage.height);
button->registerCallback(_displayButton);
button->registerCallbackFT(_setText);
button->regCallPlayVideo(_playVideo);
if (TError::isError())
{
MSG_ERROR("Dropping button because of previous errors!");
delete button;
return;
}
button->setHandle(((mSubpage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
button->createButtons();
addButton(button);
}
catch (std::exception& e)
{
MSG_ERROR("Memory exception: " << e.what());
TError::setError();
return;
}
}
else if (ename.compare("sr") == 0)
{
SR_T sr;
sr.number = atoi(reader.getAttributeFromNode(node, "number").c_str());
mxml_node_t *n = reader.getFirstChild(node);
while (n)
{
string ename = reader.getElementName(n);
if (ename.compare("bs") == 0)
sr.bs = reader.getTextFromNode(n);
else if (ename.compare("cb") == 0)
sr.cb = reader.getTextFromNode(n);
else if (ename.compare("cf") == 0)
sr.cf = reader.getTextFromNode(n);
else if (ename.compare("ct") == 0)
sr.ct = reader.getTextFromNode(n);
else if (ename.compare("ec") == 0)
sr.ec = reader.getTextFromNode(n);
else if (ename.compare("bm") == 0)
sr.bm = reader.getTextFromNode(n);
else if (ename.compare("ji") == 0)
sr.ji = reader.getIntFromNode(n);
else if (ename.compare("jb") == 0)
sr.jb = reader.getIntFromNode(n);
else if (ename.compare("fi") == 0)
sr.fi = reader.getIntFromNode(n);
else if (ename.compare("ii") == 0)
sr.ii = reader.getIntFromNode(n);
else if (ename.compare("ix") == 0)
sr.ix = reader.getIntFromNode(n);
else if (ename.compare("iy") == 0)
sr.iy = reader.getIntFromNode(n);
else if (ename.compare("oo") == 0)
sr.oo = reader.getIntFromNode(n);
n = reader.getNextChild(n);
}
mSubpage.sr.push_back(sr);
}
node = reader.getNextChild();
}
// Here the sort function could be called. But it's not necessary because
// the buttons are stored in ascending Z order. Therefor the following
// method call is commented out.
// sortButtons();
}
void TSubPage::show()
{
DECL_TRACER("TSubPage::show()");
if (!_setBackground)
{
MSG_WARNING("No callback \"setBackground\" was set!");
return;
}
ulong handle = (mSubpage.pageID << 16) & 0xffff0000;
MSG_DEBUG("Processing page " << mSubpage.pageID << ": " << mSubpage.name);
// Draw the background, if any
if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].bm.empty())
{
MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
if (rawImage)
{
SkBitmap bm;
MSG_DEBUG("Decoding image ...");
if (!DecodeDataToBitmap(rawImage, &bm))
MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
SkImageInfo info = bm.info();
size_t rowBytes = info.minRowBytes();
size_t size = info.computeByteSize(rowBytes);
MSG_DEBUG("Setting background with image of size " << size);
_setBackground(handle, (unsigned char *)bm.getPixels(), size, rowBytes, TColor::getColor(mSubpage.sr[0].cf));
}
else
{
MSG_WARNING("Couldn't read image " << mSubpage.sr[0].bm);
if (mSubpage.sr.size() > 0)
_setBackground(handle, nullptr, 0, 0, TColor::getColor(mSubpage.sr[0].cf));
}
}
else if (mSubpage.sr.size() > 0)
{
MSG_DEBUG("Calling \"setBackground\" with no image ...");
_setBackground(handle, nullptr, 0, 0, TColor::getColor(mSubpage.sr[0].cf));
}
// Draw the buttons
BUTTONS_T *button = mButtons;
while (button)
{
if (button->button)
{
MSG_DEBUG("Drawing button " << button->button->getButtonIndex() << ": " << button->button->getButtonName());
button->button->registerCallback(_displayButton);
button->button->registerCallbackFT(_setText);
button->button->regCallPlayVideo(_playVideo);
button->button->setFonts(mFonts);
button->button->setPalette(mPalette);
button->button->createButtons();
if (mSubpage.sr.size() > 0)
button->button->setGlobalOpacity(mSubpage.sr[0].oo);
button->button->show();
}
button = button->next;
}
// Mark page as visible
mVisible = true;
}
void TSubPage::drop()
{
DECL_TRACER("TSubPage::drop()");
if (mVisible && _callDropSubPage)
_callDropSubPage((mSubpage.pageID << 16) & 0xffff0000);
// Set all elements of subpage invisible
BUTTONS_T *bt = mButtons;
while (bt)
{
bt->button->hide();
bt = bt->next;
}
mZOrder = -1;
mVisible = false;
}
BUTTONS_T *TSubPage::addButton(TButton* button)
{
DECL_TRACER("*TSubPage::addButton(TButton* button)");
if (!button)
{
MSG_ERROR("Parameter is NULL!");
TError::setError();
return nullptr;
}
try
{
BUTTONS_T *chain = new BUTTONS_T;
chain->button = button;
chain->next = nullptr;
chain->previous = nullptr;
BUTTONS_T *bts = mButtons;
if (bts)
{
BUTTONS_T *p = bts;
while (p->next)
p = p->next;
p->next = chain;
chain->previous = p;
}
else
mButtons = chain;
return chain;
}
catch (std::exception& e)
{
MSG_ERROR("Memory error: " << e.what());
TError::setError();
}
return nullptr;
}
bool TSubPage::hasButton(int id)
{
DECL_TRACER("TSubPage::hasButton(int id)");
BUTTONS_T *bt = mButtons;
while (bt)
{
if (bt->button && bt->button->getButtonIndex() == id)
return true;
bt = bt->next;
}
return false;
}
TButton *TSubPage::getButton(int id)
{
DECL_TRACER("TSubPage::getButton(int id)");
BUTTONS_T *bt = mButtons;
while (bt)
{
if (bt->button && bt->button->getButtonIndex() == id)
return bt->button;
bt = bt->next;
}
return nullptr;
}
vector<TButton *> TSubPage::getButtons(int ap, int ad)
{
DECL_TRACER("TSubPage::getButtons(int ap, int ad)");
vector<TButton *> list;
BUTTONS_T *bt = mButtons;
while (bt)
{
if (bt->button->getAddressPort() == ap && bt->button->getAddressChannel() == ad)
list.push_back(bt->button);
bt = bt->next;
}
return list;
}
/*
* Sort the button according to their Z-order.
* The button with the highest Z-order will be the last button in the chain.
* The algorithm is a bubble sort algorithm.
*/
bool TSubPage::sortButtons()
{
DECL_TRACER("TSubPage::sortButtons()");
bool turned = true;
while (turned)
{
BUTTONS_T *button = mButtons;
turned = false;
while (button)
{
int zo = button->button->getZOrder();
if (button->previous)
{
if (zo < button->previous->button->getZOrder())
{
BUTTONS_T *pprev = button->previous->previous;
BUTTONS_T *prev = button->previous;
BUTTONS_T *next = button->next;
if (pprev)
pprev->next = button;
prev->next = next;
prev->previous = button;
button->next = prev;
button->previous = pprev;
if (!pprev)
mButtons = button;
button = next;
if (next)
next->previous = prev;
turned = true;
continue;
}
}
button = button->next;
}
}
return true;
}
RECT_T TSubPage::getRegion()
{
DECL_TRACER("TSubPage::getRegion()");
return {mSubpage.left, mSubpage.top, mSubpage.width, mSubpage.height};
}
/**
* This method is called indirectly from the GUI after a mouse click. If This
* subpage matches the clicked coordinates, than the elements are tested. If
* an element is found that matches the coordinates it gets the click. It
* depends on the kind of element what happens.
*/
void TSubPage::doClick(int x, int y, bool pressed)
{
DECL_TRACER("TSubPage::doClick(int x, int y)");
MSG_DEBUG("X=" << x << ", Y=" << y);
BUTTONS_T *button = mButtons;
// Find last button
while (button->next)
button = button->next;
// Scan in reverse order
while (button)
{
TButton *but = button->button;
MSG_DEBUG("Button " << but->getButtonIndex() << ": lx=" << but->getLeftPosition() << ", ly=" << but->getTopPosition() << ", rx=" << (but->getLeftPosition() + but->getWidth()) << ", ry=" << (but->getTopPosition() + but->getHeight()));
if (x >= but->getLeftPosition() && x <= (but->getLeftPosition() + but->getWidth()) &&
y >= but->getTopPosition() && y <= (but->getTopPosition() + but->getHeight()))
{
MSG_DEBUG("Clicking button " << but->getButtonIndex() << ": " << but->getButtonName() << " to state " << (pressed ? "PRESS" : "RELEASE"));
int btX = x - but->getLeftPosition();
int btY = y - but->getTopPosition();
if (but->doClick(btX, btY, pressed))
break;
}
button = button->previous;
}
}