//------------------------------------------------------------------------------
// emTextField.h
//
// Copyright (C) 2005-2010,2014,2016,2018,2021 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// 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 version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emTextField_h
#define emTextField_h

#ifndef emClipboard_h
#include <emCore/emClipboard.h>
#endif

#ifndef emBorder_h
#include <emCore/emBorder.h>
#endif


//==============================================================================
//================================ emTextField =================================
//==============================================================================

class emTextField : public emBorder {

public:

        // Class for a data field panel showing a single line of text which can
        // optionally be edited by the user. An optional multi-line mode is also
        // provided. Selection and clipboard functions are supported.

        emTextField(
                ParentArg parent, const emString & name,
                const emString & caption=emString(),
                const emString & description=emString(),
                const emImage & icon=emImage(),
                const emString & text=emString(),
                bool editable=false
        );
                // Constructor.
                // Arguments:
                //   parent      - Parent for this panel (emPanel or emView).
                //   name        - The name for this panel.
                //   caption     - The label's caption, or empty.
                //   description - The label's description, or empty.
                //   icon        - The label's icon, or empty.
                //   editable    - Whether the text can be edited by the user.

        virtual ~emTextField();
                // Destructor.

        bool IsEditable() const;
        void SetEditable(bool editable=true);
                // Whether the text can be edited by the user.

        bool GetMultiLineMode() const;
        void SetMultiLineMode(bool multiLineMode=true);
                // Whether the text may have multiple lines.

        bool GetPasswordMode() const;
        void SetPasswordMode(bool passwordMode=true);
                // Whether the text is a password that should not really be
                // shown.

        bool GetOverwriteMode() const;
        void SetOverwriteMode(bool overwriteMode=true);
                // Current mode of overwriting or inserting (can be changed with
                // the insert key).

        const emSignal & GetTextSignal() const;
                // Signaled whenever the text has changed.

        const emString & GetText() const;
        void SetText(const emString & text);
                // The text.

        int GetTextLen() const;
                // Get number of bytes in the text.

        int GetCursorIndex() const;
        void SetCursorIndex(int index);
                // Position of cursor as a byte index to the text.

        const emSignal & GetSelectionSignal() const;
                // Signaled whenever the selection has changed.

        int GetSelectionStartIndex() const;
                // Get start of selection as a byte index to the text.

        int GetSelectionEndIndex() const;
                // Get end of selection as an exclusive byte index to the text.

        bool IsSelectionEmpty() const;
                //  Whether the selection is empty.

        void Select(int startIndex, int endIndex, bool publish);
                // Set the selection.
                // Arguments:
                //   startIndex  - Start as a byte index to the text.
                //   endIndex    - End as an exclusive byte index to the text.
                //   publish     - Whether to publish the selection.

        void SelectAll(bool publish);
                // Select the whole text.
                // Arguments:
                //   publish - Whether to publish the selection.

        void EmptySelection();
                // Empty the selection.

        void PublishSelection();
                // Publish the current selection.

        void CutSelectedTextToClipboard();
                // Remove the selected text and put it to the clipboard.

        void CopySelectedTextToClipboard();
                // Copy the selected text to the clipboard.

        void PasteSelectedTextFromClipboard();
                // Paste selected text from the clipboard. If the selection is
                // empty, the text from the clipboard is inserted at the cursor
                // position. Otherwise the selected text is replaced.

        void PasteSelectedText(const emString & text);
                // Paste selected text. If the selection is empty, the given
                // text is inserted at the cursor position. Otherwise the
                // selected text is replaced.

        void DeleteSelectedText();
                // Remove the selected text.

        bool IsCursorBlinkOn() const;
                // Whether the cursor is shown at this moment.

        const emSignal & GetCanUndoRedoSignal() const;
                // Signaled whenever the result of CanUndo() or CanRedo() has changed.

        bool CanUndo() const;
                // Whether there is at least one entry in the undo buffer.

        bool CanRedo() const;
                // Whether there is at least one entry in the redo buffer.

        void Undo();
                // Undo last change if possible.

        void Redo();
                // Redo last undone change if possible.

        virtual bool Validate(int & pos, int & removeLen, emString & insertText) const;
                // This method is called whenever the text is about to change
                // (except by SetText(..)). The implementation may either accept
                // the change (return true), adapt it (modify pos, removeLen
                // and/or insertText, and return true), or reject the change
                // (return false). The default implementation uses the callback
                // function set with SetValidateFunc(..).
                // Arguments:
                //   pos        - Byte index where the text is changed.
                //   removeLen  - How many bytes to remove at position pos.
                //   insertText - The text to be inserted at position pos.
                // Returns:
                //   true on proceed with the change, false to abort.

        void SetValidateFunc(
                bool(*validateFunc)(
                        const emTextField & textField, int & pos, int & removeLen,
                        emString & insertText, void * context
                ),
                void * context=NULL
        );
                // Set a validate function. The arguments to the function are
                // like Validate(..). The context can be any pointer, it is
                // forwarded to the function.

protected:

        virtual void TextChanged();
                // Called when the text has changed.

        virtual void SelectionChanged();
                // Called when the selection has changed.

        virtual bool Cycle();
        virtual void Notice(NoticeFlags flags);
        virtual void Input(emInputEvent & event, const emInputState & state,
                           double mx, double my);

        virtual bool HasHowTo() const;
        virtual emString GetHowTo() const;

        virtual void PaintContent(
                const emPainter & painter, double x, double y, double w,
                double h, emColor canvasColor
        ) const;

        virtual bool CheckMouse(double mx, double my,
                                double * pCol, double * pRow) const;

        // - - - - - - - - - - Depreciated methods - - - - - - - - - - - - - - -
        // The following virtual non-const methods have been replaced by const
        // methods (see above). The old versions still exist here with the
        // "final" keyword added, so that old overridings will fail to compile.
        // If you run into this, please adapt your overridings by adding "const".
        virtual bool CheckMouse(double mx, double my,
                                double * pCol, double * pRow) final;
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

private:

        struct UndoEntry {
                UndoEntry * Prev;
                UndoEntry * Next;
                int Pos;
                int RemoveLen;
                emString InsertText;
        };

        struct RedoEntry {
                RedoEntry * Next;
                int Pos;
                int RemoveLen;
                emString InsertText;
        };

        enum UndoMergeType {
                UM_NO_MERGE,
                UM_BACKSPACE,
                UM_DELETE,
                UM_ALPHA_NUM,
                UM_NON_ALPHA_NUM,
                UM_NEW_LINE,
                UM_MOVE
        };

        enum ModifyFlags {
                MF_VALIDATE      = 1<<0,
                MF_CREATE_UNDO   = 1<<1,
                MF_CREATE_REDO   = 1<<2,
                MF_NO_CLEAR_REDO = 1<<3,
                MF_SELECT        = 1<<4
        };

        enum DoTextFieldFunc {
                TEXT_FIELD_FUNC_PAINT,
                TEXT_FIELD_FUNC_XY2CR,
                TEXT_FIELD_FUNC_CR2XY
        };
        void DoTextField(
                DoTextFieldFunc func, const emPainter * painter,
                emColor canvasColor,
                double xIn, double yIn, double * pXOut, double * pYOut, bool * pHit
        ) const;

        void ClearUndo();
        void ClearRedo();
        void CreateUndo(int pos, int removeLen, const emString & insertText,
                        UndoMergeType undoMerge);
        void CreateRedo(int pos, int removeLen, const emString & insertText);
        void ModifySelectedText(const emString & insertText, int modifyFlags,
                                UndoMergeType undoMerge=UM_NO_MERGE);
        void ModifyText(int pos, int removeLen, emString insertText,
                        int modifyFlags, UndoMergeType undoMerge=UM_NO_MERGE);

        enum DragModeType {
                DM_NONE,
                DM_SELECT,
                DM_SELECT_BY_WORDS,
                DM_SELECT_BY_ROWS,
                DM_INSERT,
                DM_MOVE
        };
        void SetDragMode(DragModeType dragMode);

        void RestartCursorBlinking();
        void ScrollToCursor();
        int ColRow2Index(double column, double row, bool forCursor) const;
        void Index2ColRow(int index, int * pColumn, int * pRow) const;
        void CalcTotalColsRows(int * pCols, int * pRows) const;
        int GetNormalizedIndex(int index) const;
        void ModifySelection(int oldIndex, int newIndex, bool publish);
        emMBState GetMBStateAtIndex(int index) const;
        int GetNextIndex(int index, emMBState * mbState=NULL) const;
        int GetPrevIndex(int index) const;
        int GetNextWordBoundaryIndex(int index, bool * pIsDelimiter=NULL,
                                     emMBState * mbState=NULL) const;
        int GetPrevWordBoundaryIndex(int index, bool * pIsDelimiter=NULL) const;
        int GetNextWordIndex(int index, emMBState * mbState=NULL) const;
        int GetPrevWordIndex(int index) const;
        int GetRowStartIndex(int index) const;
        int GetRowEndIndex(int index) const;
        int GetNextRowIndex(int index, emMBState * mbState=NULL) const;
        int GetPrevRowIndex(int index) const;
        int GetNextParagraphIndex(int index, emMBState * mbState=NULL) const;
        int GetPrevParagraphIndex(int index) const;

        emRef<emClipboard> Clipboard;
        emSignal TextSignal;
        emSignal SelectionSignal;
        bool Editable;
        bool MultiLineMode;
        bool PasswordMode;
        bool OverwriteMode;
        emString Text;
        int TextLen,CursorIndex,SelectionStartIndex,SelectionEndIndex;
        int MagicCursorColumn;
        emInt64 SelectionId;
        emUInt64 CursorBlinkTime;
        bool CursorBlinkOn;
        DragModeType DragMode;
        double DragPosC,DragPosR;

        UndoEntry * FirstUndo;
        UndoEntry * LastUndo;
        size_t UndoSize;
        int UndoCount;
        UndoMergeType UndoMerge;
        RedoEntry * FirstRedo;
        emSignal CanUndoRedoSignal;

        bool(*ValidateFunc)(
                const emTextField & textField, int & pos, int & removeLen,
                emString & insertText, void * context
        );
        void * ValidateFuncContext;

        static const char * const HowToTextField;
        static const char * const HowToMultiLineOff;
        static const char * const HowToMultiLineOn;
        static const char * const HowToReadOnly;
};

inline bool emTextField::IsEditable() const
{
        return Editable;
}

inline bool emTextField::GetMultiLineMode() const
{
        return MultiLineMode;
}

inline bool emTextField::GetPasswordMode() const
{
        return PasswordMode;
}

inline bool emTextField::GetOverwriteMode() const
{
        return OverwriteMode;
}

inline const emSignal & emTextField::GetTextSignal() const
{
        return TextSignal;
}

inline const emString & emTextField::GetText() const
{
        return Text;
}

inline int emTextField::GetTextLen() const
{
        return TextLen;
}

inline int emTextField::GetCursorIndex() const
{
        return CursorIndex;
}

inline const emSignal & emTextField::GetSelectionSignal() const
{
        return SelectionSignal;
}

inline int emTextField::GetSelectionStartIndex() const
{
        return SelectionStartIndex;
}

inline int emTextField::GetSelectionEndIndex() const
{
        return SelectionEndIndex;
}

inline bool emTextField::IsSelectionEmpty() const
{
        return SelectionStartIndex>=SelectionEndIndex;
}

inline bool emTextField::IsCursorBlinkOn() const
{
        return CursorBlinkOn;
}

inline const emSignal & emTextField::GetCanUndoRedoSignal() const
{
        return CanUndoRedoSignal;
}

inline bool emTextField::CanUndo() const
{
        return FirstUndo!=NULL;
}

inline bool emTextField::CanRedo() const
{
        return FirstRedo!=NULL;
}


#endif