Subversion Repositories tpanel

Rev

Rev 17 | 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
 */
#ifndef __TBUTTON_H__
#define __TBUTTON_H__

#include <string>
#include <vector>
#include <map>
#include <functional>
#include <thread>

#include <include/core/SkImage.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkBitmap.h>

#include "treadxml.h"
#include "tpalette.h"
#include "tcolor.h"
#include "tfont.h"
#include "tamxnet.h"
#include "ttimer.h"
#include "timagerefresh.h"

#define ORD_ELEM_COUNT  5

extern bool prg_stopped;

class SkFont;
struct RESOURCE_T;

namespace Button
{
    typedef enum BUTTONTYPE
    {
        NONE,
        GENERAL,
        MULTISTATE_GENERAL,
        BARGRAPH,
        MULTISTATE_BARGRAPH,
        JOISTICK,
        TEXT_INPUT,
        COMPUTER_CONTROL,
        TAKE_NOTE,
        SUBPAGE_VIEW
    } BUTTONTYPE;

    typedef struct SYSBORDER_t
    {
        int id{0};
        char *name{nullptr};
        int number{0};
        char *style{nullptr};
        int width{0};
        int radius{0};
    }SYSBORDER_t;

    typedef struct SYSBUTTONS_t
    {
        int channel{0};             // Channel number
        BUTTONTYPE type{NONE};      // Type of button
        int states{0};              // Maximum number of states
        int ll{0};                  // Level low range
        int lh{0};                  // Level high range
    }SYSBUTTONS_t;

    typedef enum TEXT_ORIENTATION
    {
        ORI_ABSOLUT,
        ORI_TOP_LEFT,
        ORI_TOP_MIDDLE,
        ORI_TOP_RIGHT,
        ORI_CENTER_LEFT,
        ORI_CENTER_MIDDLE,              // default
        ORI_CENTER_RIGHT,
        ORI_BOTTOM_LEFT,
        ORI_BOTTOM_MIDDLE,
        ORI_BOTTOM_RIGHT
    } TEXT_ORIENTATION;

    typedef enum TEXT_EFFECT
    {
        EFFECT_NONE,
        EFFECT_OUTLINE_S,
        EFFECT_OUTLINE_M,
        EFFECT_OUTLINE_L,
        EFFECT_OUTLINE_X,
        EFFECT_GLOW_S,
        EFFECT_GLOW_M,
        EFFECT_GLOW_L,
        EFFECT_GLOW_X,
        EFFECT_SOFT_DROP_SHADOW_1,
        EFFECT_SOFT_DROP_SHADOW_2,
        EFFECT_SOFT_DROP_SHADOW_3,
        EFFECT_SOFT_DROP_SHADOW_4,
        EFFECT_SOFT_DROP_SHADOW_5,
        EFFECT_SOFT_DROP_SHADOW_6,
        EFFECT_SOFT_DROP_SHADOW_7,
        EFFECT_SOFT_DROP_SHADOW_8,
        EFFECT_MEDIUM_DROP_SHADOW_1,
        EFFECT_MEDIUM_DROP_SHADOW_2,
        EFFECT_MEDIUM_DROP_SHADOW_3,
        EFFECT_MEDIUM_DROP_SHADOW_4,
        EFFECT_MEDIUM_DROP_SHADOW_5,
        EFFECT_MEDIUM_DROP_SHADOW_6,
        EFFECT_MEDIUM_DROP_SHADOW_7,
        EFFECT_MEDIUM_DROP_SHADOW_8,
        EFFECT_HARD_DROP_SHADOW_1,
        EFFECT_HARD_DROP_SHADOW_2,
        EFFECT_HARD_DROP_SHADOW_3,
        EFFECT_HARD_DROP_SHADOW_4,
        EFFECT_HARD_DROP_SHADOW_5,
        EFFECT_HARD_DROP_SHADOW_6,
        EFFECT_HARD_DROP_SHADOW_7,
        EFFECT_HARD_DROP_SHADOW_8,
        EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE,
        EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE,
        EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE,
        EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE
    }TEXT_EFFECT;

    typedef enum DRAW_ORDER
    {
        ORD_ELEM_NONE,
        ORD_ELEM_FILL,
        ORD_ELEM_BITMAP,
        ORD_ELEM_ICON,
        ORD_ELEM_TEXT,
        ORD_ELEM_BORDER
    }DRAW_ORDER;

    typedef struct SR_T
    {
        int number{0};
        std::string _do;        // Order on how to show a multistate bargraph (010203...)
        std::string bs;         // Frame type (circle, ...)
        std::string mi;         // Chameleon image
        int mi_width{0};        // Width of image
        int mi_height{0};       // Height of image
        std::string cb;         // Border color
        std::string cf;         // Fill color
        std::string ct;         // Text Color
        std::string ec;         // Text effect color
        std::string bm;         // bitmap file name
        std::string sd;         // Sound file to play
        int bm_width{0};        // Width of image
        int bm_height{0};       // Height of image
        bool dynamic{false};    // TRUE = moving image
        int sb{0};              // Index to external graphics download
        int ii{0};              // Icon index number
        int ix{0};              // Icon X position
        int iy{0};              // Icon Y position
        int ji{5};              // Icon style / position like "jt", default 5 = center+middle
        int jb{5};              // Image position (center, left, ...), default 5 = center+middle
        int bx{0};              // Absolute image position x
        int by{0};              // Absolute image position y
        int fi{0};              // Font index
        std::string te;         // Text
        TEXT_ORIENTATION jt{ORI_CENTER_MIDDLE}; // Text orientation
        int tx{0};              // Text X position
        int ty{0};              // Text Y position
        int ww{0};              // line break when 1
        int et{0};              // Text effect (^TEF)
        int oo{-1};             // Over all opacity
    } SR_T;

    typedef enum FEEDBACK
    {
        FB_NONE,
        FB_CHANNEL,
        FB_INV_CHANNEL,     // inverted channel
        FB_ALWAYS_ON,
        FB_MOMENTARY,
        FB_BLINK
    } FEEDBACK;

    typedef struct PUSH_FUNC
    {
        std::string pfType; // command to execute when button was pushed
        std::string pfName; // Name of popup
    } PUSH_FUNC_T;

    typedef enum CENTER_CODE
    {
        SC_ICON = 0,
        SC_BITMAP,
        SC_TEXT
    }CENTER_CODE;

    typedef struct POSITION_t
    {
        int width{0};
        int height{0};
        int left{1};
        int top{1};
        bool overflow{false};
        bool valid{false};
    }POSITION_t;

    typedef struct THR_REFRESH_t
    {
        ulong handle{0};
        ulong parent{0};
        int bi{0};
        TImageRefresh *mImageRefresh{nullptr};
        THR_REFRESH_t *next{nullptr};
    }THR_REFRESH_t;

    class TButton
    {
        public:
            TButton();
            ~TButton();

            /**
             * The following function parses the parameters of a particular
             * button and creates a new button. This function is called either
             * from class TPage or TSubPage when a page or subpage is created.
             *
             * xml     A pointer to the XML reader
             * node    A pointer to the actual node in the XML tree.
             */
            void initialize(TReadXML *xml, mxml_node_t *node);

            /**
             * Returns the button index. This is a unique number inside a page
             * or subpage.
             */
            int getButtonIndex() { return bi; }
            /**
             * Returns the name of the button.
             */
            std::string& getButtonName() { return na; }
            /**
             * Returns the description of the button, if there is one.
             */
            std::string& getButtonDescription() { return bd; }
            /**
             * Returns the width of the button in pixels.
             */
            int getWidth() { return wt; }
            /**
             * Returns the height of the button in pixels.
             */
            int getHeight() { return ht; }
            /**
             * Returns the left position in pixels.
             */
            int getLeftPosition() { return lt; }
            /**
             * Returns the top position of the button in pixels.
             */
            int getTopPosition() { return tp; }
            /**
             * Returns the Z-order. This number marks the order the buttons
             * are drawed on the screen. Inside a page or subpage the buttons
             * are always sorted.
             */
            int getZOrder() { return zo; }
            /**
             * Returns the type of the button.
             */
            BUTTONTYPE getButtonType() { return type; }

            int getRangeLow() { return rl; }
            int getRangeHigh() { return rh; }
            int getStateCount() { return stateCount; }
            int getAddressPort() { return ap; }
            int getAddressChannel() { return ad; }
            int getChannelNumber() { return ch; }
            int getChannelPort() { return cp; }
            int getLevelPort() { return lp; }
            int getLevelValue() { return lv; }
            int getNumberInstances() { return (int)sr.size(); }
            int getActiveInstance() { return mActInstance; }
            ulong getHandle() { return mHandle; }
            void setEnable(bool en) { mEnabled = en; }
            bool isEnabled() { return mEnabled; }
            void setHandle(ulong handle);
            void setPalette(TPalette *pal) { mPalette = pal; }
            void setParentWidth(int width) { mParentWidth = width; }
            void setParentHeight(int height) { mParentHeight = height; }
            void setParentSize(int width, int height) { mParentWidth = width; mParentHeight = height; }
            void setFonts(TFont *ft) { mFonts = ft; }
            void setGlobalOpacity(int oo) { if (oo >= 0 && oo <= 255) mGlobalOO = oo; }
            void setVisible(bool v) { visible = v; }
            bool setBitmap(const std::string& file, int instance);
            bool setOpacity(int op, int instance);
            bool setWorWrap(bool state, int instance);
            bool setFont(int id, int instance);
            void setTop(int top);
            void setLeft(int left);
            void setLeftTop(int left, int top);
            void setResourceName(const std::string& name, int instance);
            void addPushFunction(std::string& func, std::string& page);
            void registerSystemButton();
            bool isSystemButton();
            void clearPushFunctions() { pushFunc.clear(); }
            void clearPushFunction(const std::string& action);
            void refresh();
            /**
             * Sets a particular instance of the button active. This implies
             * a redraw of the button in case the instance is different from
             * the one already visible.
             *
             * @param instance
             * The instance of the button to be activated.
             *
             * @return
             * On error returns FALSE.
             */
            bool setActive(int instance);
            /**
             * Sets an Icon on the button. This implies a redraw of the button
             * in case the instance is different from the one already visible.
             *
             * @param id
             * The id number of the icon.
             *
             * @param instance
             * The instance where the icon should be drawed
             *
             * @return On error returns FALSE.
             */
            bool setIcon(int id, int instance);
            /**
             * Sets an Icon on the button. This implies a redraw of the button
             * in case the instance is different from the one already visible.
             *
             * @param icon
             * The file name of the icon.
             *
             * @param instance
             * The instance where the icon should be drawed
             *
             * @return
             * On error returns FALSE.
             */
            bool setIcon(const std::string& icon, int instance);
            /**
             * Removes an icon from a button. This implies a redraw of the
             * button.
             *
             * @param instance
             * The instance number involved.
             *
             * @return
             * On error returns FALSE.
             */
            bool revokeIcon(int instance);
            /**
             * Set a string to a button.
             *
             * @param txt
             * The text to write on top of  a button.
             *
             * @param instance
             * The instance number involved.
             *
             * @return
             * On error returns FALSE.
             */
            bool setText(const std::string& txt, int instance);
            /**
             * Read the images, if any, from files and create an image. If there
             * are no files, draw the image as defined.
             *
             * @return
             * On error returns FALSE.
             */
            bool createButtons();
            /**
             * Register a callback function to display a ready image.
             */
            void registerCallback(std::function<void (ulong handle, ulong parent, unsigned char *buffer, int width, int height, int pixline, int left, int top)> displayButton)
            {
                _displayButton = displayButton;
            }
            void registerCallbackFT(std::function<void (ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, TEXT_ORIENTATION ori, TEXT_EFFECT effect, bool ww)> setText)
            { _setText = setText; }
            void regCallPlayVideo(std::function<void (ulong handle, ulong parent, int left, int top, int width, int height, const std::string& url, const std::string& user, const std::string& pw)> playVideo) { _playVideo = playVideo; };

            /**
             * Make a pixel array and call the callback function to display the
             * image. If there is no callback function registered, nothing
             * happens.
             * This method draws a general button. It is used for most
             * specialized buttons too.
             *
             * @param instance
             * Optional. The instance of the button to draw. If the base
             * instance should be drawn, the parameter can be omitted.
             *
             * @param show
             * Optional. By defualt TRUE. If set to FALSE, the button image is
             * not send to GUI. The image is available in the variable
             * "mLastImage" immediately after ending the method.
             *
             * @return
             * On error returns FALSE.
             */
            bool drawButton(int instance = 0, bool show=true);
            /**
             * Method to draw a multistate animated button. It creates a thread
             * with a timer and starts an animation by drawing every instance in
             * the defined order.
             *
             * @return
             * On error returns FALSE.
             */
            bool drawButtonMultistateAni();
            /**
             * Draws a normal bargraph with an ON and OFF state. Takes care
             * about cameleon images. If registered, it calls a callback
             * function to the GUI to display the ready image.
             *
             * @param instance
             * The instance of the bargraph to draw. This value must be in the
             * range of 0 to "stateCount".
             *
             * @param level
             * This is the level to show on the bargraph. It must be a value
             * the range of "rl" (range low) and "rh" (range high). A lower
             * value than "rl" is interpreted as "rl" and a value higher than
             * "rh" is interpreted as "rh".
             *
             * @param show
             * Optional. By default TRUE. If set to FALSE, the bargraph image is
             * not send to GUI. The image is available in the variable
             * "mLastImage" immediately after ending the method.
             *
             * @return
             * On error returns FALSE.
             */
            bool drawBargraph(int instance, int level, bool show=true);
            /**
             * Show the button with it's current state.
             */
            void show();
            /**
             * Hide the button. In case of an animated button, the animation is
             * stopped.
             *
             * @param total
             * Optional. When set to TRUE, a transparent button is displayed.
             */
            void hide(bool total=false);
            /**
             * This method sends the image together with all data to the GUI,
             * where it will be shown. The variable mLastImage must not be
             * empty for the method to succeed.
             */
            void showLastButton();
            /**
             * Handle a mouse click.
             *
             * @param pressed
             * TRUE = Button was pressed, FALSE = Button was released.
             *
             * @return
             * TRUE = Button was clickable and processed. FALSE = Button was not
             * clickable.
             */
            bool doClick(int x, int y, bool pressed);

        protected:
            BUTTONTYPE getButtonType(const std::string& bt);
            FEEDBACK getButtonFeedback(const std::string& fb);
            SkBitmap drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2);

            void funcTimer(const amx::ANET_BLINK& blink);
            void funcNetwork(int state);
            void funcResource(const RESOURCE_T *resource, const std::string& url, SkBitmap* bm, int instance);

        private:
            typedef struct IMAGE_t
            {
                int number{0};
                SkBitmap imageMi;
                SkBitmap imageBm;

                void clear()
                {
                    number = 0;
                    imageMi.reset();
                    imageBm.reset();
                }
            }IMAGE_t;

            std::function<void (ulong handle, ulong parent, unsigned char *buffer, int width, int height, int pixline, int left, int top)> _displayButton{nullptr};
            std::function<void (ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, TEXT_ORIENTATION ori, TEXT_EFFECT effect, bool ww)> _setText{nullptr};
            std::function<void (ulong handle, ulong parent, int left, int top, int width, int height, const std::string& url, const std::string& user, const std::string& pw)> _playVideo{nullptr};

            POSITION_t calcImagePosition(int width, int height, CENTER_CODE cc, int number);
            int getBorderSize(const std::string& name);
            void calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY);
            SkColor baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2);
            TEXT_EFFECT textEffect(const std::string& effect);
            int numberLines(const std::string& str);
            std::vector<std::string> splitLine(const std::string& str);
            std::vector<std::string> splitLine(const std::string& str, int width, int height, SkFont& font, SkPaint& paint);
            SkRect calcRect(int width, int height, int pen);
            void runAnimation();    // Method started as thread for button animation

            void getDrawOrder(const std::string& sdo, DRAW_ORDER *order);
            bool buttonFill(SkBitmap *bm, int instance);
            bool buttonBitmap(SkBitmap *bm, int instance);
            bool buttonDynamic(SkBitmap *bm, int instance);
            bool buttonIcon(SkBitmap *bm, int instance);
            bool buttonText(SkBitmap *bm, int instance);
            bool buttonBorder(SkBitmap *bm, int instance);
            bool isClickable();
            bool isPixelTransparent(int x, int y);
            bool barLevel(SkBitmap *bm, int instance, int level);
            bool makeElement(int instance=-1);
            bool loadImage(SkBitmap *bm, SkBitmap& image, int instance);
            void _TimerCallback(ulong counter);
            void _imageRefresh(const std::string& url);
            static THR_REFRESH_t *_addResource(TImageRefresh *refr, ulong handle, ulong parent, int bi);
            static THR_REFRESH_t *_findResource(ulong handle, ulong parent, int bi);

            BUTTONTYPE type;
            int bi{0};              // button ID
            std::string na;         // name
            std::string bd;         // Description --> ignored
            int lt{0};              // pixel from left
            int tp{0};              // pixel from top
            int wt{0};              // width
            int ht{0};              // height
            int zo{0};              // Z-Order
            std::string hs;         // bounding, ...
            std::string bs;         // Border style (circle, ...)
            FEEDBACK fb{FB_NONE};   // momentary, ...
            int ap{1};              // Address port (default: 1)
            int ad{0};              // Address channel
            int ch{0};              // Channel number
            int cp{1};              // Channel port (default: 1)
            int lp{1};              // Level port (default: 1)
            int lv{0};              // Level code
            std::string dr;         // Level "horizontal" or "vertical"
            int va{0};
            int stateCount{0};      // State count with multistate buttons
            int rm{0};              // State count with multistate buttons?
            int nu{2};              // Animate time up
            int nd{2};              // Animate time down
            int ar{0};              // Auto repeat (1 = true)
            int ru{0};              // Animate time up (bargraph)
            int rd{0};              // Animate time down (bargraph)
            int lu{0};              // Animate time up (Bargraph)
            int ld{0};              // Animate time down (Bargraph)
            int rv{0};
            int rl{0};              // Range low
            int rh{0};              // Range high
            int ri{0};              // Bargraph inverted (0 = normal, 1 = inverted)
            int rn{0};              // Bargraph: Range drag increment
            std::string _if;        // Bargraph function: empty = display only, active, active centering, drag, drag centering
            std::string sd;         // Name/Type of slider for a bargraph
            std::string sc;         // Color of slider (for bargraph)
            int mt{0};              // Length of text area (0 = 2000)
            std::string dt;         // "multiple" textarea has multiple lines, else single line
            std::string im;         // Input mask of a text area
            std::string op;         // String the button send
            bool visible{true};     // TRUE=Button is visible
            std::vector<PUSH_FUNC_T> pushFunc;  // Push functions: This are executed on button press
            std::vector<SR_T> sr;   // The elements the button consists of
            TPalette *mPalette{nullptr}; // The color palette
            // Image management
            std::map<int, IMAGE_t> mImages; // Contains the images in ready to use format, if there any
            SkBitmap mLastImage;    // The last calculated image
            ulong mHandle{0};       // internal used handle to identify button
            int mParentHeight{0};   // The height of the parent page / subpage
            int mParentWidth{0};    // The width of the parent page / subpage
            bool mEnabled{true};    // By default a button is enabled (TRUE); FALSE = Button disabled
            TFont *mFonts{nullptr}; // The font table
            int mGlobalOO{-1};      // Opacity of the whole subpage, if any
            int mActInstance{0};    // Active instance
            DRAW_ORDER mDOrder[ORD_ELEM_COUNT];  // The order to draw the elements of a button
            std::thread mThrAni;    // Thread handle for animation
            std::thread mThrRes;    // A resouce (download of a remote image/video) running in background
            static std::atomic<bool> mAniRunning; // TRUE = Animation is running
            int mLastLevel{0};      // The last level value for a bargraph
            bool mSystemReg{false}; // TRUE = registered as system button
            amx::ANET_BLINK mLastBlink; // This is used for the system clock buttons
            TTimer *mTimer{nullptr};    // This is for buttons displaying the time or a date. It's a thread running in background.
            static THR_REFRESH_t *mThrRefresh;  // If  we have a source to reread periodicaly, this starts a thread to do that.
            bool mResourceRead{false};  // TRUE if the resource should be read only once on startup and this is done.
    };

    typedef struct BUTTONS_T
    {
        TButton *button{nullptr};
        BUTTONS_T *previous{nullptr};
        BUTTONS_T *next{nullptr};
    } BUTTONS_T;
}

#endif