Subversion Repositories tpanel

Rev

Rev 16 | 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "tresources.h"
#include "tpage.h"

using std::string;
using namespace Button;

TPage::TPage(const string& name)
{
    DECL_TRACER("TPage::TPage(const string& name)");
    TError::clear();
    initialize(name);
}

TPage::~TPage()
{
    DECL_TRACER("TPage::~TPage()");

    MSG_DEBUG("Destroing page " << pageID << ": " << name);
    BUTTONS_T *p = mButtons;
    BUTTONS_T *next = nullptr;

    while (p)
    {
        next = p->next;
        delete p->button;
        delete p;
        p = next;
    }

    mButtons = nullptr;

    PAGECHAIN_T *pc = mSubPages;
    PAGECHAIN_T *pc_next = nullptr;

    // We're not allowd to delete the subpages here, because they're managed
    // by the TPageManager.
    while (pc)
    {
        pc_next = pc->next;
        delete pc;
        pc = pc_next;
    }

    mSubPages = nullptr;
}

void TPage::initialize(const string& nm)
{
    DECL_TRACER("TPage::initialize(const string& name)");
    makeFileName(TConfig::getProjectPath(), nm);

    if (isValidFile())
        mPath = getFileName();

    TReadXML reader(mPath);

    if (TError::isError())
        return;

    reader.findElement("page", "type");

    if (!reader.success())
    {
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found!");
        TError::setError();
        return;
    }

    string type = reader.getAttribute("type");

    if (type.compare("page") != 0)
    {
        MSG_ERROR("Invalid page type \"" << type << "\"!");
        TError::setError();
        return;
    }

    mxml_node_t *node = reader.getFirstChild();

    while (node)
    {
        string e = reader.getElementName(node);

        if (e.compare("pageID") == 0)
            pageID = reader.getIntFromNode(node);
        else if (e.compare("name") == 0)
            name = reader.getTextFromNode(node);
        else if (e.compare("width") == 0)
            width = reader.getIntFromNode(node);
        else if (e.compare("height") == 0)
            height = reader.getIntFromNode(node);
        else if (e.compare("button") == 0)
        {
            TButton *button = new TButton();
            button->setPalette(mPalette);
            button->setFonts(mFonts);
            button->registerCallback(_displayButton);
            button->registerCallbackFT(_setText);
            button->regCallPlayVideo(_playVideo);
            button->initialize(&reader, node);

            if (TError::isError())
            {
                delete button;
                return;
            }

            button->setHandle(((pageID << 16) & 0xffff0000) | button->getButtonIndex());
            button->createButtons();
            addButton(button);
        }
        else if (e.compare("sr") == 0)
        {
            SR_T bsr;
            bsr.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)
                    bsr.bs = reader.getTextFromNode(n);
                else if (ename.compare("cb") == 0)
                    bsr.cb = reader.getTextFromNode(n);
                else if (ename.compare("cf") == 0)
                    bsr.cf = reader.getTextFromNode(n);
                else if (ename.compare("ct") == 0)
                    bsr.ct = reader.getTextFromNode(n);
                else if (ename.compare("ec") == 0)
                    bsr.ec = reader.getTextFromNode(n);
                else if (ename.compare("bm") == 0)
                    bsr.bm = reader.getTextFromNode(n);
                else if (ename.compare("fi") == 0)
                    bsr.fi = reader.getIntFromNode(n);

                n = reader.getNextChild(n);
            }

            sr.push_back(bsr);
        }

        node = reader.getNextChild();
    }

    sortButtons();
}

void TPage::show()
{
    DECL_TRACER("TPage::show()");

    if (!_setBackground)
    {
        MSG_WARNING("No callback \"setBackground\" was set!");
        return;
    }

    ulong handle = (pageID << 16) & 0xffff0000;
    MSG_DEBUG("Processing page " << pageID);

    if (!sr[0].bm.empty())
    {
        sk_sp<SkData> rawImage = readImage(sr[0].bm);

        if (rawImage)
        {
            SkBitmap bm;
            DecodeDataToBitmap(rawImage, &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(sr[0].cf));
            return;
        }
        else
        {
            MSG_WARNING("Couldn't read image " << sr[0].bm);
        }
    }
    else
    {
        MSG_DEBUG("No background image defined!");
    }

    MSG_DEBUG("Calling \"setBackground\" with no image ...");
    _setBackground(handle, nullptr, 0, 0, TColor::getColor(sr[0].cf));
    mVisible = true;
}

PAGECHAIN_T *TPage::addSubPage(TSubPage* pg)
{
    DECL_TRACER("TPage::addSubPage(TSubPage* pg)");

    if (!pg)
    {
        MSG_ERROR("Parameter is NULL!");
        TError::setError();
        return nullptr;
    }

    PAGECHAIN_T *chain = new PAGECHAIN_T;
    chain->subpage = pg;
    chain->next = nullptr;
    PAGECHAIN_T *spg = mSubPages;

    if (spg)
    {
        // First make sure that the new page is not already in the chain.
        PAGECHAIN_T *p = spg;

        while (p)
        {
            if (p->subpage->getNumber() == pg->getNumber())
            {
                MSG_TRACE("Page " << pg->getNumber() << " is already in chain. Don't add it again.");
                delete chain;
                return p;
            }

            p = p->next;
        }

        // The subpage is not in chain. So we add it now.
        p = spg;
        // Find the last element in chain
        while (p->next)
            p = p->next;

        p->next = chain;
    }
    else
    {
        mZOrder = 0;
        mSubPages = chain;
    }

    mLastSubPage = 0;
    return chain;
}

bool TPage::removeSubPage(int ID)
{
    DECL_TRACER("TPage::removeSubPage(int ID)");

    PAGECHAIN_T *p = mSubPages;
    PAGECHAIN_T *prev = nullptr;

    while (p)
    {
        if (p->subpage->getNumber() == ID)
        {
            PAGECHAIN_T *next = p->next;

            if (prev)
                prev->next = next;
            else
                mSubPages = next;

            delete p;
            mLastSubPage = 0;
            return true;
        }

        prev = p;
        p = p->next;
    }

    return false;
}

bool TPage::removeSubPage(const std::string& nm)
{
    DECL_TRACER("TPage::removeSubPage(const std::string& nm)");

    PAGECHAIN_T *p = mSubPages;
    PAGECHAIN_T *prev = nullptr;

    while (p)
    {
        if (p->subpage->getName().compare(nm) == 0)
        {
            PAGECHAIN_T *next = p->next;

            if (prev)
                prev->next = next;
            else
                mSubPages = next;

            delete p;
            mLastSubPage = 0;
            return true;
        }

        prev = p;
        p = p->next;
    }

    return false;
}

TSubPage *TPage::getSubPage(int pageID)
{
    DECL_TRACER("TPage::getSubPage(int pageID)");

    PAGECHAIN_T *pg = mSubPages;

    while (pg)
    {
        if (pg->subpage->getNumber() == pageID)
        {
            mLastSubPage = pageID;
            return pg->subpage;
        }

        pg = pg->next;
    }

    mLastSubPage = 0;
    return nullptr;
}

TSubPage *TPage::getSubPage(const std::string& name)
{
    DECL_TRACER("TPage::getSubPage(const std::string& name)");

    PAGECHAIN_T *pg = mSubPages;

    while (pg)
    {
        if (pg->subpage->getName().compare(name) == 0)
        {
            mLastSubPage = pg->subpage->getNumber();
            return pg->subpage;
        }

        pg = pg->next;
    }

    mLastSubPage = 0;
    return nullptr;
}

TSubPage *TPage::getFirstSubPage()
{
    DECL_TRACER("TPage::getFirstSubPage()");

    PAGECHAIN_T *pg = mSubPages;

    if (pg)
    {
        if (pg->subpage)
        {
            mLastSubPage = pg->subpage->getNumber();
            MSG_DEBUG("Subpage " << pg->subpage->getNumber() << ". " << pg->subpage->getName());
            return pg->subpage;
        }
    }

    MSG_DEBUG("No subpages in chain.");
    mLastSubPage = 0;
    return nullptr;
}

TSubPage *TPage::getNextSubPage()
{
    DECL_TRACER("TPage::getNextSubPage()");

    if (mLastSubPage > 0)
    {
        PAGECHAIN_T *p = mSubPages;

        while (p)
        {
            if (p->subpage->getNumber() == mLastSubPage)
            {
                if (p->next && p->next->subpage)
                {
                    TSubPage *page = p->next->subpage;
                    mLastSubPage = page->getNumber();
                    MSG_DEBUG("Subpage " << page->getNumber() << ". " << page->getName());
                    return page;
                }
            }

            p = p->next;
        }
    }

    MSG_DEBUG("No more subpages in chain.");
    mLastSubPage = 0;
    return nullptr;
}

BUTTONS_T *TPage::addButton(TButton* button)
{
    DECL_TRACER("*TPage::addButton(TButton* button)");

    if (!button)
    {
        MSG_ERROR("Parameter is NULL!");
        TError::setError();
        return nullptr;
    }

    BUTTONS_T *chain = new BUTTONS_T;
    BUTTONS_T *bts = mButtons;
    chain->button = button;

    if (bts)
    {
        BUTTONS_T *p = bts;

        while (p->next)
            p = p->next;

        p->next = chain;
        chain->previous = p;
    }
    else
        mButtons = chain;

    return chain;
}

bool TPage::hasButton(int id)
{
    DECL_TRACER("TPage::hasButton(int id)");

    BUTTONS_T *bt = mButtons;

    while (bt)
    {
        if (bt->button && bt->button->getButtonIndex() == id)
            return true;

        bt = bt->next;
    }

    return false;
}

TButton *TPage::getButton(int id)
{
    DECL_TRACER("TPage::getButton(int id)");

    BUTTONS_T *bt = mButtons;

    while (bt)
    {
        if (bt->button && bt->button->getButtonIndex() == id)
            return bt->button;

        bt = bt->next;
    }

    return nullptr;
}

std::vector<TButton *> TPage::getButtons(int ap, int ad)
{
    DECL_TRACER("TSubPage::getButtons(int ap, int ad)");

    std::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;
}

void TPage::drop()
{
    DECL_TRACER("TPage::drop()");

    PAGECHAIN_T *pc = mSubPages;

    while (pc)
    {
        pc->subpage->drop();
        pc = pc->next;
    }

    mZOrder = ZORDER_INVALID;
    mVisible = false;
}

/*
 * 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 TPage::sortButtons()
{
    DECL_TRACER("TPage::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;
}