root/libcore/TextField.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
//   2011 Free Software Foundation, Inc
// 
// 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 St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef GNASH_TEXTFIELD_H
#define GNASH_TEXTFIELD_H

#include <boost/intrusive_ptr.hpp>
#include <map>
#include <string>
#include <vector>

#include "InteractiveObject.h" // for inheritance
#include "LineStyle.h" // for LineStyle
#include "snappingrange.h"
#include "SWFRect.h" // for inlines

// Forward declarations
namespace gnash {
    namespace SWF {
        class DefineEditTextTag;
        class TextRecord;
    }
    class TextFormat_as;
    class Font;
}

namespace gnash {

/// An instance of a DefineEditTextTag 
class TextField : public InteractiveObject
{

public:
    
    typedef std::vector<size_t> LineStarts;

    /// Text alignment values
        enum TextAlignment
        {
                ALIGN_LEFT = 0,
                ALIGN_RIGHT,
                ALIGN_CENTER,
                ALIGN_JUSTIFY
        };

        /// Text format display values
        enum TextFormatDisplay
        {
                TEXTFORMAT_BLOCK = 0,
                TEXTFORMAT_INLINE = 1
        };
        
        /// Possible autoSize values
        enum AutoSize {

                /// Do not automatically resize TextField as text grow/shrink
                AUTOSIZE_NONE,

                /// Expand TextField, anchor the top-left side
                AUTOSIZE_LEFT,

                /// Expand TextField, anchor the horizontal center
                AUTOSIZE_CENTER,

                /// Expand TextField, anchor the top-right side
                AUTOSIZE_RIGHT
        };

        /// Possible type values
        enum TypeValue {

                /// Invalid value
                typeInvalid,

                /// Do not accept input, text is only changed by variable name
                /// or assigning to the .text member
                typeDynamic,

                /// Accept user input
                typeInput
        };

    /// Constructs a TextField as specified in a DefineEditText tag.
        TextField(as_object* object, DisplayObject* parent,
            const SWF::DefineEditTextTag& def);

    /// Constructs a TextField with default values and the specified bounds.
    //
    /// Notably, the default textHeight is 12pt (240 twips).
        /// @param parent A pointer to the DisplayObject parent of this TextField
        /// @param bounds A SWFRect specifying the bounds of this TextField
    TextField(as_object* object, DisplayObject* parent, const SWFRect& bounds);


        ~TextField();

        // TODO: should this return isSelectable() ?
        /// Returns true for now, TextField is always "Mouse-Enabled"
        bool mouseEnabled() const { return true; }

        /// Returns a pointer to the topmost InteractiveObject at (x,y)
        //
        /// @param x x-coordinate
        /// @param y y-coordinate
        InteractiveObject* topmostMouseEntity(boost::int32_t x, boost::int32_t y);

    /// Return the version of the SWF this was parsed from.
    //
    /// TODO: work out what this means for dynamic TextFields.
    virtual int getDefinitionVersion() const;

        /// This function is called as a user-input handler
        void notifyEvent(const event_id& id);   

        const std::string& getVariableName() const
        {
                return _variable_name;
        }

        /// Set the name of a variable associated to this
        /// TextField's displayed text.
        //
        /// Calling this function will override any previous
        /// setting for the variable name.
        /// 
        void set_variable_name(const std::string& newname);
        
        /// \brief Set our text to the given string by effect of an update of a
    /// registered variable name
        //
        /// This call only updates the text and is only meant to be called
    /// by ourselves or by MovieClip when a registered TextVariable is
    /// updated.
        void updateText(const std::string& s);

        /// Return value of our text.
        std::string get_text_value() const;

        /// Return value of our htmlText.
        std::string get_htmltext_value() const;

        /// Return true if text is defined
        bool getTextDefined() const { return _textDefined; }

    size_t getCaretIndex() const {
        return m_cursor;
    }

        /// Get a std::pair of size_t with start/end of selection
    //
    /// Both start and end should invariably be within the
    /// range of the text.
    const std::pair<size_t, size_t>& getSelection() const {
        return _selection;
    }

    /// Replace the current selection with the new text.
        //
        /// @param replace String to replace the current selection
    void replaceSelection(const std::string& replace);

    /// Set the current selection
    //
    /// @param start    The index of the beginning of the selection.
    /// @param end      The index of the end of the selection.
    //
    /// If start is greater than end, the values are swapped, ensuring
    /// end is never less than start.
    void setSelection(int start, int end);

    /// Override of DisplayObject::setWidth
    //
    /// TextField width currently behaves differently from MovieClip width
    virtual void setWidth(double width);
    
    /// Override of DisplayObject::setHeight
    //
    /// TextField height currently behaves differently from MovieClip height
    virtual void setHeight(double height);

        /// Draw the dynamic string.
        virtual void display(Renderer& renderer, const Transform& xform);

        void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);

        /// Get bounding SWFRect of this TextField
        virtual SWFRect getBounds() const
        {
                return _bounds;
        }

        // See dox in DisplayObject.h
        bool pointInShape(boost::int32_t x, boost::int32_t y) const;

        /// Return true if the 'background' should be drawn
        bool getDrawBackground() const;

        /// Specify whether to draw the background
        //
        /// @param draw If true the background of this TextField will be drawn
        void setDrawBackground(bool draw);

        /// \brief Return color of the background
        rgba getBackgroundColor() const;

        /// Set color of the background
        //
        /// Use setDrawBackground to actually use this value.
        /// @param col RGBA Object with color information. TextField
        ///     background will be drawn in this color
        void setBackgroundColor(const rgba& col);

        /// Return true if this TextField should have its border visible
        bool getDrawBorder() const;

        /// Specify whether to draw the border
        //
        /// @param draw If true the border of this TextField will be drawn
        void setDrawBorder(bool draw);

        /// Return color of the border
        rgba getBorderColor() const;

        /// Set color of the border
        //
        /// Use setDrawBorder to actually use this value.
        /// @param col RGBA Object with color information. TextField border
        ///     will be drawn in this color.
        void setBorderColor(const rgba& col);

        /// Return color of the text
        const rgba& getTextColor() const 
        {
                return _textColor;
        }

        /// Set color of the text
        //
        /// @param col RGBA Object with color information. Text in this TextField
        ///     will be displayed in this color.
        void setTextColor(const rgba& col);

        /// \brief
        /// Return true if this TextField should use embedded font glyphs,
        /// false if it should use device font glyphs
        bool getEmbedFonts() const {
                return _embedFonts;
        }

    /// Get the current maxChars setting of the TextField
    boost::int32_t maxChars() const {
        return _maxChars;
    }

    /// Set the current maxChars setting of the TextField
        //
        /// @param max The maximum number of characters that can be
        ///     input by the user (Does not restrict Scripts)
    void maxChars(boost::int32_t max) {
        _maxChars = max;
    }

    /// Get the current multiline setting of the TextField
    bool multiline() const {
        return _multiline;
    }

    /// Set the current multiline setting of the TextField
        //
        /// @param b If true "Enter" key will be recognized (Does not
        ///     restrict Scripts)
    void multiline(bool b) {
        _multiline = b;
    }
        
    /// Get the current password setting of the TextField
    bool password() const {
        return _password;
    }

    /// Set the current password setting of the TextField
        //
        /// @param b If true characters in the TextField will be displayed
        ///     as (*)
    void password(bool b) {
        _password = b;
    }
        /// \brief
        /// Set whether this TextField should use embedded font glyphs,
        /// or use device font glyphs
        //
        /// @param use
        void setEmbedFonts(bool use);

        /// Get autoSize value 
        AutoSize getAutoSize() const
        {
                return _autoSize;
        }

        /// Return text TextAlignment
    TextAlignment getTextAlignment();

        /// Set autoSize value 
        //
        /// @param val
        ///     The AutoSize to use
        ///
        void setAutoSize(AutoSize val);

        /// Set type (input or dynamic)
        //
        /// @param val
        ///     The TypeValue to use, no-op if typeInvalid.
        ///
        void setType(TypeValue val) { if (val != typeInvalid) _type=val; }

        /// Get type (input, dynamic or invalid)
        TypeValue getType() const
        {
                return _type;
        }

        /// Return true if this TextField is read-only
        bool isReadOnly() const { return _type != typeInput; }

        /// Parse type string value
        //
        /// @param val
        ///     Type value as a string (one of input or dynamic)
        ///
        /// @return an TypeValue identifier. typeInvalid if invalid.
        ///
        static TypeValue parseTypeValue(const std::string& val);

        /// Return type value as a string
        //
        /// @param val
        ///     Type value  (enum)
        ///
        /// @return a C-string representation of the type value.
        ///     The returns is *never* NULL.
        ///
        static const char* typeValueName(TypeValue val);

        /// \brief
        /// Return true if text should continue to next available line
        /// when hitting end of bounding box.
        ///
        bool doWordWrap() const {
                return _wordWrap;
        }

        /// Set wordWrap parameter 
        //
        /// @param on
        ///     If true text hitting bounding box limits will continue
        ///     to next line.
        ///     If false, either text will be truncated or bounding box
        ///     expanded, depending on autoSize (see getAutoSize)
        ///
        void setWordWrap(bool on);

        /// Return true if HTML markup in text should be rendered.
        bool doHtml() const {
                return _html;
        }

        /// Set html parameter
        //
        /// @param on
        ///     If true HTML tags in the text will be parsed and rendered
        void setHtml(bool on) {
                _html = on;
        }

        /// Return true if the TextField text is selectable
        bool isSelectable() const
        {
                return _selectable;
        }

        /// Set 'selectable' parameter
        //
        /// @param v
        ///     If true text in this TextField will be selectable
        void setSelectable(bool v) 
        {
                _selectable = v;
        }

        // See DisplayObject::isActiveTextField
        /// Return true if the TextField text is selectable
        virtual bool isSelectableTextField() const
        {
                return isSelectable();
        }

        /// Remove this textfield from the stage
        //
        /// This is to implement TextField.removeTextField, will
        /// basically forward the request to its parent.
        /// Eventually this and MovieClip::removeMovieClip
        /// will be merged in a single function to be later used
        /// also for AS3 removeChild().
        ///
        void removeTextField();

        /// Set our font, return previously set one.
        //
        /// @param newfont
        ///     Will be stored in an intrusive_ptr
        ///
        boost::intrusive_ptr<const Font> setFont(
            boost::intrusive_ptr<const Font> newfont);

        const Font* getFont() { return _font.get(); }

        
        boost::uint16_t getFontHeight() const
        {
                return _fontHeight;
        }

        void setFontHeight(boost::uint16_t h);

        boost::uint16_t getLeftMargin() const
        {
                return _leftMargin;
        }

        void setLeftMargin(boost::uint16_t h);

        boost::uint16_t getRightMargin() const
        {
                return _rightMargin;
        }

        void setRightMargin(boost::uint16_t h);

        boost::uint16_t getIndent() const
        {
                return _indent;
        }

        void setIndent(boost::uint16_t h);

        boost::uint16_t getBlockIndent() const
        {
                return _blockIndent;
        }

        void setBlockIndent(boost::uint16_t h);

        TextAlignment getAlignment() const
        {
                return _alignment;
        }

        void setAlignment(TextAlignment h);

        boost::int16_t getLeading() const
        {
                return _leading;
        }

        void setLeading(boost::int16_t h);

        bool getUnderlined() const
        {
                return _underlined;
        }
        
        TextFormatDisplay getDisplay() const
        { 
                return _display;
        }
        
        bool getBullet() const
        {
                return _bullet;
        }
        
        const std::vector<int>& getTabStops() const
        {
                return _tabStops;
        }

        bool isRestrict() const
        {
                return _restrictDefined;
        }
        
        const std::string& getRestrict() const
        {
                return _restrict;
        }

        size_t getScroll() const
        {
                return _scroll;
        }

        size_t getMaxScroll() const
        {
                return _maxScroll;
        }

        size_t getHScroll() const
        {
                return _hScroll;
        }

        size_t getMaxHScroll() const
        {
                return _maxHScroll;
        }

        size_t getBottomScroll() const
        {
                return _bottomScroll;
        }

        void setUnderlined(bool v);
        void setTabStops(const std::vector<int>& tabStops);
        void setBullet(bool b);
        void setURL(std::string url);
        void setTarget(std::string target);
        void setRestrict(const std::string& restrict);
        void setDisplay(TextFormatDisplay display);
        void setScroll(size_t scroll) {
                _scroll = scroll;
                format_text();
        }
        void setMaxScroll(size_t maxScroll) {
                _maxScroll = maxScroll;
                format_text();
        }
        void setHScroll(size_t hScroll) {
                _hScroll = hScroll;
                format_text();
        }
        void setMaxHScroll(size_t maxHScroll) {
                _maxHScroll = maxHScroll;
                format_text();
        }
        void setbottomScroll(size_t bottomScroll) {
                _bottomScroll = bottomScroll;
                format_text();
        }

        /// Returns the number of the record that the cursor is in
        //
        size_t cursorRecord();

        void setTextFormat(TextFormat_as& tf);

        const SWFRect& getTextBoundingBox() const {
                return m_text_bounding_box;
        }

        /// Set our text to the given string.
        //
        /// This function will also update any registered variable
        ///
        void setTextValue(const std::wstring& wstr);

private:

    void init();

        /// \brief Set our text to the given string by effect of an update of a
    /// registered variable name
        //
        /// This call only updates the text and is only meant to be called
    /// by ourselves or by MovieClip when a registered TextVariable is
    /// updated.
        void updateText(const std::wstring& s);

        void updateHtmlText(const std::wstring& s);

    void insertTab(SWF::TextRecord& rec, boost::int32_t& x, float scale);

        /// What happens when setFocus() is called on this TextField.
    //
    /// @return true if focus was set. A TextField can always receive focus,
    /// so this always returns true.
        virtual bool handleFocus();

        /// Kill focus 
        virtual void killFocus();

        /// Call this function when willing to invoke the onChanged event handler
        void onChanged();

        /// Reset our text bounding box to the given point.
        void reset_bounding_box(boost::int32_t x, boost::int32_t y)
        {
                m_text_bounding_box.set_to_point(x, y);
        }

        /// Convert the DisplayObjects in _text into a series of
        /// text_glyph_records to be rendered.
        void format_text();
        
        /// Move viewable lines based on m_cursor
        void scrollLines();
        
        /// Handles a new line, this will be called several times, so this
        /// will hopefully make code cleaner
        void newLine(boost::int32_t& x, boost::int32_t& y, 
                                 SWF::TextRecord& rec, int& last_space_glyph,
                                 LineStarts::value_type& last_line_start_record, float div);
                                        
        /// De-reference and do appropriate action for character iterator
        void handleChar(std::wstring::const_iterator& it,
            const std::wstring::const_iterator& e, boost::int32_t& x,
            boost::int32_t& y, SWF::TextRecord& rec, int& last_code,
                    int& last_space_glyph,
            LineStarts::value_type& last_line_start_record);
        
        /// Extracts an HTML tag.
        ///
        /// @param tag  This string is filled with the extracted HTML tag.
        /// @param attributes This is a map of attribute names and values
        /// @param it   An iterator pointing to the first DisplayObject of the
        ///             HTML tag. It is left pointing to the DisplayObject after the
        ///             closing tag or the end of the string.
        /// @param e    An iterator pointing to the end of the string.
        /// @return     Whether the tag is complete or not (i.e. whether a '>'
        ///             was found).
        bool parseHTML(std::wstring& tag,
            std::map<std::string, std::string>& attributes,
            std::wstring::const_iterator& it,
            const std::wstring::const_iterator& e,
                        bool& selfclosing) const;

        /// Does LEFT/CENTER/RIGHT alignment on the records in
        /// m_text_glyph_records[], starting with
        /// last_line_start_record and going through the end of
        /// m_text_glyph_records.
        float align_line(TextAlignment align, int last_line_start_record, float x);

        /// Associate a variable to the text of this DisplayObject
        //
        /// Setting the associated variable actually changes the
        /// displayed text. Getting the variable would return the
        /// displayed text.
        ///
        /// If the given variable already exist use it to set
        /// current text before overriding it.
        ///
        /// Since the variable target may be undefined at time
        /// of instantiation of this EditText DisplayObject, the
        /// class keeps track of wheter it succeeded registering
        /// the variable and this function will do nothing in this
        /// case. Thus it is safe to call it multiple time, using
        /// an as-needed policy (will be called from get_text_value and
        /// display)
        ///
        void registerTextVariable();

        typedef std::pair<as_object*, ObjectURI> VariableRef;

        /// \brief
        /// Parse the given variable name
        /// into sprite and a string_table::key components
        ///
        VariableRef parseTextVariableRef(const std::string& variableName) const;

        /// Called in display(), sets the cursor using m_cursor and _textRecords
        //
        /// @param renderer
        /// @param mat
        void show_cursor(Renderer& renderer, const SWFMatrix& mat);

    /// The immutable definition of our TextField
    //
    /// This is NULL for dynamic TextFields.
    boost::intrusive_ptr<const SWF::DefineEditTextTag> _tag;

        /// The actual text.
    //
    /// Because we have to deal with non-ascii DisplayObjects (129-255), this
    /// is a wide string; the cursor position and the position within the
    /// string are then the same, which makes manipulating the string much
    /// easier.
        std::wstring _text;

        /// The html representation of our text
        //
        std::wstring _htmlText;

        /// bounds of dynamic text, as laid out
        SWFRect m_text_bounding_box;

        typedef std::vector<SWF::TextRecord> TextRecords;
        TextRecords _textRecords;

        std::vector<size_t> _recordStarts;

        TextRecords _displayRecords;

        std::string _url;
        std::string _target;
        std::string _restrict;
        std::set<wchar_t> _restrictedchars;
        TextFormatDisplay _display;
        std::vector<int> _tabStops;
        LineStarts _line_starts;

        /// The text variable name
        //
        /// This is stored here, and not just in the definition,
        /// because it can be changed programmatically, by setting
        /// 'TextFields.variable'
        std::string _variable_name;

        rgba _backgroundColor;

        rgba _borderColor;

        rgba _textColor;
        
        TextAlignment _alignment;

        boost::intrusive_ptr<const Font> _font;
        size_t m_cursor;
        size_t _glyphcount;
        size_t _scroll;
        size_t _maxScroll;
        size_t _hScroll;
        size_t _maxHScroll;
        size_t _bottomScroll;
        size_t _linesindisplay;

    /// Corresponds to the maxChars property.
    size_t _maxChars;
        
    AutoSize _autoSize;

        TypeValue _type;

        /// Area in which the text is drawn. 
        //
        /// This area encloses all the text, can be automatically
        /// extended to fit text or hide text overflowing it.
        /// See the setAutoSize() method to change that.
        ///
        SWFRect _bounds;

    /// Represents the selected part of the text. The second element must
    /// never be less than the first.
    std::pair<size_t, size_t> _selection;

    boost::int16_t _leading;
        boost::uint16_t _indent;

        /// Indentation for every line (including the ones created by
        /// effect of a word-wrap.
        boost::uint16_t _blockIndent;

        boost::uint16_t _leftMargin;

        boost::uint16_t _rightMargin;

        boost::uint16_t _fontHeight;

        /// This flag will be true as soon as the TextField
        /// is assigned a text value. Only way to be false is
        /// when definition has the hasText flag set to false
        /// and no actionscript added text.
        bool _textDefined;

        bool _restrictDefined;
        bool _underlined;
        bool _bullet;

        bool m_has_focus;
        

    /// Corresponds to the multiline property.
    bool _multiline;

    /// Corresponds to the password property.
    bool _password;
        
        /// The flag keeping status of TextVariable registration
        //
        /// It will be set to true if there's no need to register
        /// a text variable (ie. non-specified in the SWF)
        ///
        bool _text_variable_registered;

        bool _drawBackground;

        bool _drawBorder;

        bool _embedFonts;

        bool _wordWrap;

        bool _html;

        bool _selectable;
        
};

} // namespace gnash

#endif 

/* [<][>][^][v][top][bottom][index][help] */