/* libsswf_tag_edittext.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2008 */

/*

Copyright (c) 2002-2008 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

/** \file
 *
 * \brief The implementation of the sswf::TagEditText class
 *
 * This file declares the body of the functions which are not
 * inline. It is part of the SSWF library.
 */

#include	"sswf/libsswf.h"

using namespace sswf;





/////////////////////////////////////////// TagEditText


/** \class sswf::TagEditText
 *
 * \brief Defines a dynamic edit text box.
 *
 * This tag is used to create a text box. People can write
 * text in that box unless it is marked as read-only. Using
 * an action scripts it is also possible to modify the text
 * defined in an edit text box.
 *
 * One important feature is the use of embedded fonts. In
 * that case, you probably want to mark the edit text with
 * a set of glyphs which need to be included (or "*" for
 * all the glyphs of the font.)
 *
 * \sa sswf::TagFont
 * \sa sswf::TagText
 * \sa <a href="../SWFalexref.html#tag_defineedittext">SWF Alexis' Reference&mdash;DefineEditText</a>
 * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference&mdash;swf_tag</a>
 */



/** \enum sswf::TagEditText::edit_text_alignment_t
 *
 * \brief Defines the different possible alignment.
 *
 * This enumaration defines the list of possible alignment
 * in an edit text box.
 */

/** \var sswf::TagEditText::EDIT_TEXT_ALIGNMENT_LEFT
 *
 * \brief Defines the left alignment
 *
 * This value is used to have the text rendered on the left
 * edge.
 *
 * This is the default value.
 */

/** \var sswf::TagEditText::EDIT_TEXT_ALIGNMENT_RIGHT
 *
 * \brief Defines the right alignment
 *
 * This value is used to have the text rendered on the
 * right edge.
 */

/** \var sswf::TagEditText::EDIT_TEXT_ALIGNMENT_CENTER
 *
 * \brief Defines the centered alignment
 *
 * This value is used to have the text horizontally rendered
 * in the center of the edit text box.
 */

/** \var sswf::TagEditText::EDIT_TEXT_ALIGNMENT_JUSTIFY
 *
 * \brief Defines the justified alignment
 *
 * This value is supposed to have to text aligned on the
 * left and right edges adding some space where spaces are
 * rendered so it looks like it fits exactly.
 *
 * This may work in the current version, but it has not
 * been working in older versions (v4, v5).
 */



/** \brief Initializes the object.
 *
 * The constructor ensures that all the variables are set to
 * their default.
 *
 * \param[in] parent The parent must be a TagHeader
 */
TagEditText::TagEditText(TagBase *parent)
	: TagBaseID("edit", parent)
{
	//f_bounds = ...
	f_align = EDIT_TEXT_ALIGNMENT_LEFT;
	f_left_margin = 0;
	f_right_margin = 0;
	f_indent = 0;
	f_leading = 0;
	f_font = 0;
	f_font_height = 0;
	f_max_length = 0;
	f_text = 0;
	f_var_name = 0;
	f_used_glyphs = 0;
	f_used_strings = 0;
	//f_color = ...
	f_has_color = false;
	f_word_wrap = true;		// by default most people probably want word wrap (that's all editors default! right?)
	f_multiline = false;
	f_password  = false;
	f_readonly  = false;
	f_no_select = false;
	f_border    = true;		// that's the usual purpose of such an object!
	f_outline   = false;
}


/** \brief Returns the type of an edit text box.
 *
 * This function returns the type of an edit text box.
 *
 * An edit text box is a definition and a reference.
 *
 * \return SWF_TYPE_DEFINE and SWF_TYPE_REFERENCE
 */
TagBase::swf_type_t TagEditText::TypeFlags(void) const
{
	return SWF_TYPE_DEFINE | SWF_TYPE_REFERENCE;
}


/** \brief Check that the edit text box can be saved and in which version.
 *
 * The function checks whether a font was defined for the
 * edit text. If so, it marks all the glyphs in use according
 * to those specified via the TagEditText::SetUsedGlyphs() and
 * TagEditText::AddUsedString() function calls.
 *
 * Errors are generated if a required glyph does not exist in
 * the corresponding font.
 *
 * \sa sswf::TagEditText::Save(Data& data)
 * \sa sswf::TagEditText::SetUsedGlyphs(const char *used_glyphs)
 * \sa sswf::TagEditText::AddUsedString(const char *string)
 */
ErrorManager::error_code_t TagEditText::PreSave(void)
{
	const sswf_ucs4_t		*s;
	char				wname[16];
	TagFont::font_info_t		info;
	TagFont				*font;
	size_t				l, sz;
	sswf_ucs4_t			*d, *str;
	ErrorManager::error_code_t	err_code;

	PreSaveCSMTextSettings();

	MinimumVersion(f_autosize ? 6 : 4);

	if(f_font == 0 || !f_outline) {
		return ErrorManager::ERROR_CODE_NONE;
	}

	font = const_cast<TagFont *>(f_font);
	font->SetUsedByEditText();

	// if there are no glyphs, forget the following code
	if(!font->HasGlyph()) {
		return ErrorManager::ERROR_CODE_NONE;
	}

	err_code = ErrorManager::ERROR_CODE_NONE;

	// We can't be sure whether the "used strings" below also includes the
	// initialization text so we simply make sure all of the initialization
	// text glyphs are included too.
	if(f_text != 0) {
		// here we need the string in wide chars
		l = strlen(f_text);
		str = (sswf_ucs4_t *) MemAlloc(sizeof(sswf_ucs4_t) * (l + 1), "TagEditText::PreSave() -- temporary buffer to convert the text in wide characters");

		sz = l * sizeof(sswf_ucs4_t);
		d = str;
		mbtowc(f_text, l, d, sz);
		*d = (sswf_ucs4_t) '\0';	// mbtowc() doesn't terminate the strings

		d = str;
		while(*d != '\0') {
			info.f_glyph = *d;
			if(!font->FindGlyph(info, true)) {
				err_code = OnError(ErrorManager::ERROR_CODE_NO_SUCH_GLYPH, "TagEditText: the character %s does not exist in the font named \"%s\". (2)\n", wcname(info.f_glyph, wname), font->FontName());
			}
			d++;
		}
		MemFree(str);
	}

	if(f_used_strings != 0) {
		s = f_used_strings;
		while(*s != '\0') {
			info.f_glyph = *s;
			if(font->FindGlyph(info, true)) {
				err_code = OnError(ErrorManager::ERROR_CODE_NO_SUCH_GLYPH, "TagEditText: the character %s does not exist in the font named \"%s\". (3)\n", wcname(info.f_glyph, wname), font->FontName());
			}
			s++;
		}
		// if there is a used string we don't want to call
		// the SetUsedGlyphs() with an empty 'used glyphs'
		// string (which would defaults to "*")
		if(f_used_glyphs == 0) {
			return err_code;
		}
		if(*f_used_glyphs == '\0') {
			return err_code;
		}
	}

	return font->SetUsedGlyphs(f_used_glyphs, true);
}





/** \brief Save an edit text box in a Data buffer.
 *
 * This function saves an edit text box in the specified
 * Data buffer.
 *
 * \param[in] data The Data buffer where the edit text box is saved
 *
 * \return An error or ErrorManager::ERROR_CODE_NONE
 */
ErrorManager::error_code_t TagEditText::Save(Data& data)
{
	Data				sub_data;
	bool				has_layout;
	ErrorManager::error_code_t	ec;

// save this object ID
	SaveID(sub_data);

// save rectangle where text appears
	f_bounds.Save(sub_data);

// save all the flags
	sub_data.Align();
	sub_data.WriteBits(f_text != 0, 1);
	sub_data.WriteBits(f_word_wrap, 1);
	sub_data.WriteBits(f_multiline, 1);
	sub_data.WriteBits(f_password, 1);
	sub_data.WriteBits(f_readonly, 1);
	sub_data.WriteBits(f_has_color, 1);
	sub_data.WriteBits(f_max_length > 0, 1);
	sub_data.WriteBits(f_font != 0, 1);
	sub_data.WriteBits(0, 1);		// reserved
	sub_data.WriteBits(f_autosize, 1);	// auto-size
	has_layout =	   f_align != 0
			|| f_left_margin != 0
			|| f_right_margin != 0
			|| f_indent != 0
			|| f_leading != 0;
	sub_data.WriteBits(has_layout, 1);
	sub_data.WriteBits(f_no_select, 1);
	sub_data.WriteBits(f_border, 1);
	sub_data.WriteBits(0, 1);		// reserved
	sub_data.WriteBits(f_html, 1);

	sub_data.WriteBits(f_outline, 1);
	//sub_data.WriteBits(0, 1);		// considered as reserved - setting to 1 makes the plugins crash!

	if(f_font != 0) {
		f_font->SaveID(sub_data);
		sub_data.PutShort(f_font_height);
	}

	if(f_has_color) {
		f_color.Save(sub_data, true);
	}

	if(f_max_length > 0) {
		sub_data.PutShort((short) f_max_length);
	}

	if(has_layout) {
		sub_data.PutByte((char) f_align);
		sub_data.PutShort((short) f_left_margin);
		sub_data.PutShort((short) f_right_margin);
		sub_data.PutShort((short) f_indent);
		sub_data.PutShort((short) f_leading);
	}

	ec = SaveString(sub_data, f_var_name);

	if(f_text != 0) {
		ec = ErrorManager::KeepFirst(ec, SaveString(sub_data, f_text));
	}

// put the tag and size in front of all of that
	SaveTag(data, SWF_TAG_TEXT_FIELD, sub_data.ByteSize());
	data.Append(sub_data);

// if defined, also save the TagCSMTextSettings
	ec = ErrorManager::KeepFirst(ec, SaveCSMTextSettings(data));

	return ec;
}


/** \brief Set the text to be rendered on screen.
 *
 * This function sets the text that will be rendered on the screen.
 *
 * The glyphs of the default text are automatically marked as
 * required for this animation.
 *
 * \sa sswf::TagEditText::AddUsedString(const char *string)
 */
void TagEditText::SetText(const char *text)
{
	MemFree(f_text);
	f_text = StrDup(text);
}


/** \brief Set the name of the variable.
 *
 * This function is used to mark this edit text box with a
 * name. This name can later be used in action scripts to
 * read and set the text of the edit text box.
 *
 * \param[in] name The name of the variable
 */
void TagEditText::SetVariableName(const char *name)
{
	MemFree(f_var_name);
	f_var_name = StrDup(name);
}


/** \brief Mark a set of glyphs as in use.
 *
 * This function can be used to mark a set of glyphs as in use.
 *
 * The special string "*" or a NULL pointer will request the
 * function to mark all the glyphs as being in use.
 *
 * The special character - (dash/minus) is used to define a range
 * unless it is found at the very beginning or the very end of
 * the string. For instance, to include all the upper case latin 1
 * letters, one can use "A-Z".
 *
 * The use of this function is not cumulative. Calling it again
 * overwrites the previous glyphs in use.
 *
 * \bug
 * This glyphs marked in used must be marked as such after the
 * PreSave() function of the corresponding TagFont is called.
 * For this reason, we must saved this information here.
 *
 * \sa sswf::TagEditText::AddUsedString(const char *string)
 */
void TagEditText::SetUsedGlyphs(const char *used_glyphs)
{
	sswf_ucs4_t	*d;
	size_t		l, sz;

	// replace whatever was defined so far
	MemFree(f_used_glyphs);

	// NOTE: we allocate strlen() chars in the wide char string
	// because that's much faster than computing the number of
	// chars in a C string than in a UTF-8 string and we won't
	// lose that much RAM anyway

	l = strlen(used_glyphs);

	f_used_glyphs = (sswf_ucs4_t *) MemAlloc(sizeof(sswf_ucs4_t) * (l + 1), "TagEditText::SetUsedGlyphs() -- used glyphs entry string buffer");

	sz = l * sizeof(sswf_ucs4_t);
	d = f_used_glyphs;
	mbtowc(used_glyphs, l, d, sz);
	*d = (sswf_ucs4_t) '\0';	// mbtowc() doesn't terminate the strings
}


/** \brief Add a string that will be used with that edit text box.
 *
 * This function is used to tell the edit text box that it will
 * need to be able to display the specified string.
 *
 * This can be used to obtimize the number of glyphs included in
 * your movie. If your edit text box cannot be edited by an end
 * user, then it is sufficient to only save the glyphs that will
 * at some point be used and forget about the others (especially
 * to save some space in your file.)
 *
 * For instance, if you have a set of action scripts testing
 * different values and setting the edit text to pre-defined
 * strings such as "Running", "Stopped", "Waiting" and "Paused"
 * then you do not need to save the 'z' letter for instance.
 *
 * Calling this function multiple times cumulates the strings.
 *
 * \param[in] string A string to add to the list of strings in use
 */
void TagEditText::AddUsedString(const char *string)
{
	sswf_ucs4_t	*old, *d;
	size_t		l1, l2, sz;

	old = f_used_strings;

	// NOTE: we allocate strlen() chars in the wide char string
	// because that's much faster than computing the number of
	// chars in a C string than in a UTF-8 string and we won't
	// lose that much RAM anyway

	l1 = wcslen(f_used_strings);
	l2 = strlen(string);

	f_used_strings = (sswf_ucs4_t *) MemAlloc(sizeof(sswf_ucs4_t) * (l1 + l2 + 1), "TagEditText::AddUsedString() -- used string entry string buffer");

	memcpy(f_used_strings, old, sizeof(sswf_ucs4_t) * l1);
	// now delete the old string
	MemFree(old);

	// concatenate the new string
	sz = l2 * sizeof(sswf_ucs4_t);
	d = f_used_strings + l1;
	mbtowc(string, l2, d, sz);
	*d = (sswf_ucs4_t) '\0';	// mbtowc() doesn't terminate the strings
}


/** \brief Set the font to use to render the glyphs.
 *
 * This function defines the font to use with the edit text box
 * and the height we want the font to be rendered at.
 *
 * \bug
 * Note that if the font is a system font then the height has to
 * be in system font metrics (such as 14 for a 14 points font.)
 * When the font is an embedded font, then the height is taken
 * in Twips.
 *
 * \param[in] font The font tag used to render this edit text box.
 * \param[in] height The height to use to draw the characters.
 */
void TagEditText::SetFont(const TagFont *font, long height)
{
	f_font = font;
	f_font_height = (unsigned short) height;
}


/** \brief Defines the side margin
 *
 * This function defines the left and right margin for the text
 * to be rendered in the box.
 *
 * The margins are useful if you request a border and/or edge
 * to be rendered.
 *
 * \param[in] left The width of the margin on the left side
 * \param[in] right The width of the margin on the right side
 */
void TagEditText::SetMargins(long left, long right)
{
	f_left_margin = left;
	f_right_margin = right;
}


/** \brief Set the bounds of the edit text.
 *
 * This function takes a rectangle representing the bounds
 * of the edit text in the output.
 *
 * \param[in] bounds The bounds of the edit text box.
 */
void TagEditText::SetBounds(const SRectangle& bounds)
{
	f_bounds = bounds;
}


/** \brief Set the alignment for the text.
 *
 * This function sets the alignment for the text drawn
 * in the edit text box.
 *
 * The alignment can be set to one of these:
 *
 * \li TagEditText::EDIT_TEXT_ALIGNMENT_LEFT (default)
 * \li TagEditText::EDIT_TEXT_ALIGNMENT_RIGHT
 * \li TagEditText::EDIT_TEXT_ALIGNMENT_CENTER
 * \li TagEditText::EDIT_TEXT_ALIGNMENT_JUSTIFY
 *
 * \param[in] align The alignment to use
 */
void TagEditText::SetAlign(edit_text_alignment_t align)
{
	f_align = align;
}


/** \brief Set the identation of the first line of text.
 *
 * This alue represents the identation of the first line
 * of text in a paragraph.
 *
 * \param[in] indent The identation of the text
 */
void TagEditText::SetIndent(long indent)
{
	f_indent = indent;
}


/** \brief Set the identation of the first line of text.
 *
 * This alue represents the number of pixels (in TWIPS) to skip
 * vertically between lines of text.
 *
 * \param[in] leading The space between lines.
 */
void TagEditText::SetLeading(long leading)
{
	f_leading = leading;
}


/** \brief The color used to render the font.
 *
 * This function is used to set the color to use to render the
 * font.
 *
 * Note that you can later use the font as a mask over
 * pretty much any other drawings giving an effect that
 * looks like colorful fonts.
 *
 * \param[in] color The color to use to draw the font
 */
void TagEditText::SetColor(Color& color)
{
	f_color = color;
	f_has_color = true;
}


/** \brief Set the maximum length of the text in number of characters.
 *
 * If the text is editable, to avoid letting people type as much as they
 * want, use this function and set the limit you want. It is wise to
 * always do so.
 *
 * \note
 * This is supposed to be counted in characters. Encodings like UTF-8
 * or some asian languages may not be able to handling this properly/
 *
 * \param[in] length The maximum length a user can type
 */
void TagEditText::SetMaxLength(long length)
{
	f_max_length = length;
}


/** \brief Set whether words should wrap.
 *
 * This function is used to change the word wrap status.
 *
 * By default an edit text box is set to have its text wrap.
 *
 * \param[in] status Set to true to get words to wrap, false otherwise
 */
void TagEditText::SetWordWrap(bool status)
{
	f_word_wrap = status;
}


/** \brief Set the multiline flag to the specified status.
 *
 * This function changes the behavior of the edit text box
 * whenever strings longer than what can be rendered are
 * used in an edit text.
 */
void TagEditText::SetMultiline(bool status)
{
	f_multiline = status;
}


/** \brief Set the password flag.
 *
 * By default, an edit text box is expected to be used for
 * other purposes than passwords. But it can be useful, once
 * in a while, to let people login. For that purpose, you
 * can use this function.
 *
 * The result of setting this flag to true is that the edit text
 * box only renders asterisks,
 *
 * \param[in] status If true, the edit text box only displays
 * asterisks.
 */
void TagEditText::SetPassword(bool status)
{
	f_password = status;
}


/** \brief Mark the edit text box as read-only.
 *
 * This function marks the edit text box as read-only. This
 * means it can be used to display a string and the user has
 * to right to change its content.
 *
 * \param[in] status The new status for the read-only flag.
 */
void TagEditText::SetReadOnly(bool status)
{
	f_readonly = status;
}


/** \brief Set whether the user can select the text.
 *
 * This function is used to mark the text in the edit text box as
 * selectable or not. In general, an edit text box used for a
 * label should be marked unselectable and read-only.
 *
 * \param[in] status The new status for the no-select flag.
 */
void TagEditText::SetNoSelect(bool status)
{
	f_no_select = status;
}


/** \brief Mark whether a border should be rendered.
 *
 * This function requests that a border be drawn around the
 * edit text box.
 *
 * Note that the border color is usually black, and the
 * background is always white.
 *
 * \bug
 * If your text is white, it will not show up.
 *
 * \param[in] status If true, draw a background and edge
 */
void TagEditText::SetBorder(bool status)
{
	f_border = status;
}


/** \brief Set whether an outline should be rendered.
 *
 * This flag defines whether an embedded font should be used.
 *
 * \param[in] status Set to true to use a system default font
 */
void TagEditText::SetOutline(bool status)
{
	f_outline = status;
}


/** \brief Defines the box as using HTML or plain text.
 *
 * The text in the edit text box can be set to Plain Text (the
 * default) or HTML.
 *
 * When set to HTML, a few tags are understood:
 *
 * \li &lt;a>...&lt;/a>
 * \li &lt;b>...&lt;/b>
 * \li &lt;br>
 * \li &lt;font>...&lt;/font>
 * \li &lt;i>...&lt;/i>
 * \li &lt;li>...&lt;/li>
 * \li &lt;p>...&lt;/p>
 * \li &lt;tab>
 * \li &lt;textformat>...&lt;/textformat>
 * \li &lt;u>...&lt;/u>
 *
 * For more information about the supported HTML, please, see SWF Alexis' Reference.
 *
 * \param[in] status If set to true, the text in the box is expected to be HTML.
 *
 * \sa <a href="../SWFalexref.html#tag_defineedittext">SWF Alexis' Reference&mdash;DefineEditText</a>
 */
void TagEditText::SetHTML(bool status)
{
	f_html = status;
}


/** \brief Automatically resizing the edit text box.
 *
 * This function defines whether the box should be automatically
 * resized or the size is defined statically.
 *
 * \param[in] status If true, the box will resize itself around the defined text
 */
void TagEditText::SetAutoSize(bool status)
{
	f_autosize = status;
}







/* The following options fold the documentation; use 'zi' to turn on and off
 *
 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
 */
