Torque3D Documentation / _generateds / guiMLTextCtrl.cpp

guiMLTextCtrl.cpp

Engine/source/gui/controls/guiMLTextCtrl.cpp

More...

Public Functions

ConsoleDocClass(GuiMLTextCtrl , "@brief A text <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that uses the Gui Markup Language ('ML') tags <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically change the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "Example of dynamic changes include colors, styles , and/or hyperlinks. These changes can occur without " "having <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use separate text controls with separate text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">profiles.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a>(CenterPrintText)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lineSpacing=\"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    allowColorChars = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    maxChars = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    deniedSound = \"DeniedSoundProfile\";\n" "    text = \"The Text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> This Control.\";\n" "    useURLMouseCursor = \"true\";\n" "    //Properties not specific <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> have been omitted from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">example.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiCore\n</a>" )
DefineEngineFunction(StripMLControlChars , const char * , (const char *inString) , "@brief Strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters from the specified string, returning <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 'clean' <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">version.\n\n</a>" " @param inString <a href="/coding/class/classstring/">String</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define the string <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from\n</a>" "% string)
DefineEngineMethod(GuiMLTextCtrl , addText , void , (const char *text, bool reformat) , (true) , "@brief Appends the text in the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> with additional text. Also .\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param text New text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> append <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@param reformat If true, the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> will also be visually reset(defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">add\n</a>" "% text)
DefineEngineMethod(GuiMLTextCtrl , forceReflow , void , () , "@brief Forces the text <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> reflow the text after <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text is added, possibly resizing the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">add\n</a>" "% newText)
DefineEngineMethod(GuiMLTextCtrl , getText , const char * , () , "@brief Returns the text from the control, including TorqueML <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the text displayed in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "% controlText, including any TorqueML <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n\n</a>" " @see <a href="/coding/class/classguicontrol/">GuiControl</a>" )
DefineEngineMethod(GuiMLTextCtrl , scrollToBottom , void , () , "@brief Scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the bottom of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bottom\n</a>" "%thisGuiMLTextCtrl.scrollToBottom();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/classguicontrol/">GuiControl</a>" )
DefineEngineMethod(GuiMLTextCtrl , scrollToTag , void , (S32 tagID) , "@brief Scroll down <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tag.\n\n</a>" "Detailed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">description\n\n</a>" "@param tagID TagID <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the TagID we want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "% tagId)
DefineEngineMethod(GuiMLTextCtrl , scrollToTop , void , () , "@brief Scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the top of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">top\n</a>" "%thisGuiMLTextCtrl.scrollToTop();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/classguicontrol/">GuiControl</a>" )
DefineEngineMethod(GuiMLTextCtrl , setAlpha , void , (F32 alphaVal) , "@brief Sets the alpha <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param alphaVal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a> - 1.0 floating <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">alpha\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the alphe <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a>" "% alphaVal)
DefineEngineMethod(GuiMLTextCtrl , setCursorPosition , bool , (S32 newPos) , "@brief Change the text <a href="/coding/file/sdlcursorcontroller_8cpp/#sdlcursorcontroller_8cpp_1a06e8dd1f849973ccc456f8553601e8b9">cursor</a>'s position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> defined offset within the text in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param newPos <a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a660d8e8336a58d3d6950e2a638583056">Offset</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> place <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">cursor.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define <a href="/coding/file/sdlcursorcontroller_8cpp/#sdlcursorcontroller_8cpp_1a06e8dd1f849973ccc456f8553601e8b9">cursor</a> offset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" "% position)
DefineEngineMethod(GuiMLTextCtrl , setText , void , (const char *text) , "@brief Set the text contained in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param text The text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> display in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">display\n</a>" "% text)
getHexVal(char c)
IMPLEMENT_CALLBACK(GuiMLTextCtrl , onResize , void , (S32 width, S32 maxY) , (width, maxY) , "@brief Called whenever the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes.\n\n</a>" "@param width The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> width <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "@param maxY The current maximum allowed Y <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Control <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> changed, causing the callback <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n</a>" "GuiMLTextCtrl::onResize(%this,%width,%maxY)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "//Code <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> call when the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes\n</a>" " }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" )
IMPLEMENT_CALLBACK(GuiMLTextCtrl , onURL , void , (const char *url) , (url) , "@brief Called whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> URL was clicked on within the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param url The URL address that was clicked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// A URL address was clicked on in the control, causing the callback <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n</a>" "GuiMLTextCtrl::onUrl(%this,%url)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "//Code <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> run whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> URL was clicked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on\n</a>" " }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" )
bool
scanforchar(const char * str, U32 & idx, char c)
bool
scanforURL(const char * str, U32 & idx, char c)

Detailed Description

Public Functions

ConsoleDocClass(GuiMLTextCtrl , "@brief A text <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that uses the Gui Markup Language ('ML') tags <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dynamically change the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "Example of dynamic changes include colors, styles , and/or hyperlinks. These changes can occur without " "having <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use separate text controls with separate text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">profiles.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a>(CenterPrintText)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lineSpacing=\"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    allowColorChars = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    maxChars = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "    deniedSound = \"DeniedSoundProfile\";\n" "    text = \"The Text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> This Control.\";\n" "    useURLMouseCursor = \"true\";\n" "    //Properties not specific <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> have been omitted from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">example.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiCore\n</a>" )

DefineEngineFunction(StripMLControlChars , const char * , (const char *inString) , "@brief Strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters from the specified string, returning <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 'clean' <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">version.\n\n</a>" " @param inString <a href="/coding/class/classstring/">String</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define the string <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> strip TorqueML <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> characters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from\n</a>" "% string)

DefineEngineMethod(GuiMLTextCtrl , addText , void , (const char *text, bool reformat) , (true) , "@brief Appends the text in the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> with additional text. Also .\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param text New text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> append <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@param reformat If true, the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> will also be visually reset(defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> true).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">add\n</a>" "% text)

DefineEngineMethod(GuiMLTextCtrl , forceReflow , void , () , "@brief Forces the text <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> reflow the text after <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text is added, possibly resizing the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">add\n</a>" "% newText)

DefineEngineMethod(GuiMLTextCtrl , getText , const char * , () , "@brief Returns the text from the control, including TorqueML <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the text displayed in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "% controlText, including any TorqueML <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n\n</a>" " @see <a href="/coding/class/classguicontrol/">GuiControl</a>" )

DefineEngineMethod(GuiMLTextCtrl , scrollToBottom , void , () , "@brief Scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the bottom of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bottom\n</a>" "%thisGuiMLTextCtrl.scrollToBottom();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/classguicontrol/">GuiControl</a>" )

DefineEngineMethod(GuiMLTextCtrl , scrollToTag , void , (S32 tagID) , "@brief Scroll down <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tag.\n\n</a>" "Detailed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">description\n\n</a>" "@param tagID TagID <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the TagID we want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "% tagId)

DefineEngineMethod(GuiMLTextCtrl , scrollToTop , void , () , "@brief Scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the top of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform <a href="/coding/class/classguimltextctrl/">GuiMLTextCtrl</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> scroll <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">top\n</a>" "%thisGuiMLTextCtrl.scrollToTop();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/classguicontrol/">GuiControl</a>" )

DefineEngineMethod(GuiMLTextCtrl , setAlpha , void , (F32 alphaVal) , "@brief Sets the alpha <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param alphaVal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a> - 1.0 floating <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">alpha\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the alphe <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a>" "% alphaVal)

DefineEngineMethod(GuiMLTextCtrl , setCursorPosition , bool , (S32 newPos) , "@brief Change the text <a href="/coding/file/sdlcursorcontroller_8cpp/#sdlcursorcontroller_8cpp_1a06e8dd1f849973ccc456f8553601e8b9">cursor</a>'s position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> defined offset within the text in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param newPos <a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a660d8e8336a58d3d6950e2a638583056">Offset</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> place <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">cursor.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define <a href="/coding/file/sdlcursorcontroller_8cpp/#sdlcursorcontroller_8cpp_1a06e8dd1f849973ccc456f8553601e8b9">cursor</a> offset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" "% position)

DefineEngineMethod(GuiMLTextCtrl , setText , void , (const char *text) , "@brief Set the text contained in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param text The text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> display in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the text <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">display\n</a>" "% text)

getHexVal(char c)

GFX_ImplementTextureProfile(GFXMLTextureProfile , GFXTextureProfile::DiffuseMap , GFXTextureProfile::PreserveSize</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da23d780ef2bcc57521a4945415ce5207f">GFXTextureProfile::Static , GFXTextureProfile::NONE )

IMPLEMENT_CALLBACK(GuiMLTextCtrl , onResize , void , (S32 width, S32 maxY) , (width, maxY) , "@brief Called whenever the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes.\n\n</a>" "@param width The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> width <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "@param maxY The current maximum allowed Y <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Control <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> changed, causing the callback <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n</a>" "GuiMLTextCtrl::onResize(%this,%width,%maxY)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "//Code <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> call when the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes\n</a>" " }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" )

IMPLEMENT_CALLBACK(GuiMLTextCtrl , onURL , void , (const char *url) , (url) , "@brief Called whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> URL was clicked on within the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param url The URL address that was clicked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// A URL address was clicked on in the control, causing the callback <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">occur.\n</a>" "GuiMLTextCtrl::onUrl(%this,%url)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "//Code <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> run whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> URL was clicked <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on\n</a>" " }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControl\n\n</a>" )

IMPLEMENT_CONOBJECT(GuiMLTextCtrl )

scanforchar(const char * str, U32 & idx, char c)

scanforURL(const char * str, U32 & idx, char c)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "gui/controls/guiMLTextCtrl.h"
  25#include "gui/containers/guiScrollCtrl.h"
  26#include "console/consoleTypes.h"
  27#include "gui/core/guiCanvas.h"
  28#include "gfx/gfxDevice.h"
  29#include "gfx/gfxDrawUtil.h"
  30#include "core/frameAllocator.h"
  31#include "core/strings/unicode.h"
  32#include "sfx/sfxSystem.h"
  33#include "sfx/sfxTrack.h"
  34#include "sfx/sfxTypes.h"
  35#include "console/engineAPI.h"
  36
  37IMPLEMENT_CONOBJECT( GuiMLTextCtrl );
  38
  39ConsoleDocClass( GuiMLTextCtrl,
  40   "@brief A text control that uses the Gui Markup Language ('ML') tags to dynamically change the text.\n\n"
  41
  42   "Example of dynamic changes include colors, styles, and/or hyperlinks. These changes can occur without "
  43   "having to use separate text controls with separate text profiles.\n\n"
  44
  45   "@tsexample\n"
  46   "new GuiMLTextCtrl(CenterPrintText)\n"
  47   "{\n"
  48   "    lineSpacing = \"2\";\n"
  49   "    allowColorChars = \"0\";\n"
  50   "    maxChars = \"-1\";\n"
  51   "    deniedSound = \"DeniedSoundProfile\";\n"
  52   "    text = \"The Text for This Control.\";\n"
  53   "    useURLMouseCursor = \"true\";\n"
  54   "    //Properties not specific to this control have been omitted from this example.\n"
  55   "};\n"
  56   "@endtsexample\n\n"
  57
  58   "@see GuiControl\n\n"
  59
  60   "@ingroup GuiCore\n"
  61);
  62
  63
  64IMPLEMENT_CALLBACK( GuiMLTextCtrl, onURL, void, ( const char* url ),( url ),
  65   "@brief Called whenever a URL was clicked on within the control.\n\n"
  66   "@param url The URL address that was clicked on.\n"
  67   "@tsexample\n"
  68   "// A URL address was clicked on in the control, causing the callback to occur.\n"
  69   "GuiMLTextCtrl::onUrl(%this,%url)\n"
  70   "  {\n"
  71   "     // Code to run whenever a URL was clicked on\n"
  72   "  }\n"
  73   "@endtsexample\n\n"
  74   "@see GuiControl\n\n"
  75);
  76
  77IMPLEMENT_CALLBACK( GuiMLTextCtrl, onResize, void, ( S32 width, S32 maxY ),( width, maxY ),
  78   "@brief Called whenever the control size changes.\n\n"
  79   "@param width The new width value for the control\n"
  80   "@param maxY The current maximum allowed Y value for the control\n\n"
  81   "@tsexample\n"
  82   "// Control size changed, causing the callback to occur.\n"
  83   "GuiMLTextCtrl::onResize(%this,%width,%maxY)\n"
  84   "  {\n"
  85   "     // Code to call when the control size changes\n"
  86   "  }\n"
  87   "@endtsexample\n\n"
  88   "@see GuiControl\n\n"
  89);
  90
  91GFX_ImplementTextureProfile(GFXMLTextureProfile,
  92                            GFXTextureProfile::DiffuseMap, 
  93                            GFXTextureProfile::PreserveSize |
  94                            GFXTextureProfile::Static, 
  95                            GFXTextureProfile::NONE);
  96
  97const U32 GuiMLTextCtrl::csmTextBufferGrowthSize = 1024;
  98
  99DefineEngineMethod( GuiMLTextCtrl, setText, void, (const char* text),,
 100   "@brief Set the text contained in the control.\n\n"
 101   "@param text The text to display in the control.\n"
 102   "@tsexample\n"
 103   "// Define the text to display\n"
 104   "%text = \"Nifty Control Text\";\n\n"
 105   "// Set the text displayed within the control\n"
 106   "%thisGuiMLTextCtrl.setText(%text);\n"
 107   "@endtsexample\n\n"
 108   "@see GuiControl")
 109{
 110   object->setText(text, dStrlen(text));
 111}
 112
 113DefineEngineMethod( GuiMLTextCtrl, getText, const char*, (),,
 114   "@brief Returns the text from the control, including TorqueML characters.\n\n"
 115   "@tsexample\n"
 116   "// Get the text displayed in the control\n"
 117   "%controlText = %thisGuiMLTextCtrl.getText();\n"
 118   "@endtsexample\n\n"
 119   "@return Text string displayed in the control, including any TorqueML characters.\n\n"
 120   "@see GuiControl")
 121{
 122   return( object->getTextContent() );
 123}
 124
 125DefineEngineMethod( GuiMLTextCtrl, addText, void, ( const char* text, bool reformat), (true),
 126   "@brief Appends the text in the control with additional text. Also .\n\n"
 127   "@param text New text to append to the existing text.\n"
 128   "@param reformat If true, the control will also be visually reset (defaults to true).\n"
 129   "@tsexample\n"
 130   "// Define new text to add\n"
 131   "%text = \"New Text to Add\";\n\n"
 132   "// Set reformat boolean\n"
 133   "%reformat = \"true\";\n\n"
 134   "// Inform the control to add the new text\n"
 135   "%thisGuiMLTextCtrl.addText(%text,%reformat);\n"
 136   "@endtsexample\n\n"
 137   "@see GuiControl")
 138{
 139   object->addText(text, dStrlen(text), reformat);
 140}
 141
 142DefineEngineMethod( GuiMLTextCtrl, setCursorPosition, bool, (S32 newPos),,
 143   "@brief Change the text cursor's position to a new defined offset within the text in the control.\n\n"
 144   "@param newPos Offset to place cursor.\n"
 145   "@tsexample\n"
 146   "// Define cursor offset position\n"
 147   "%position = \"23\";\n\n"
 148   "// Inform the GuiMLTextCtrl object to move the cursor to the new position.\n"
 149   "%thisGuiMLTextCtrl.setCursorPosition(%position);\n"
 150   "@endtsexample\n\n"
 151   "@return Returns true if the cursor position moved, or false if the position was not changed.\n\n"
 152   "@see GuiControl")
 153{
 154   return object->setCursorPosition(newPos);
 155}
 156
 157DefineEngineMethod( GuiMLTextCtrl, scrollToTag, void, (S32 tagID),,
 158   "@brief Scroll down to a specified tag.\n\n"
 159   "Detailed description\n\n"
 160   "@param tagID TagID to scroll the control to\n"
 161   "@tsexample\n"
 162   "// Define the TagID we want to scroll the control to\n"
 163   "%tagId = \"4\";\n\n"
 164   "// Inform the GuiMLTextCtrl to scroll to the defined TagID\n"
 165   "%thisGuiMLTextCtrl.scrollToTag(%tagId);\n"
 166   "@endtsexample\n\n"
 167   "@see GuiControl")
 168{
 169   object->scrollToTag( tagID );
 170}
 171
 172DefineEngineMethod( GuiMLTextCtrl, scrollToTop, void, (),,
 173   "@brief Scroll to the top of the text.\n\n"
 174   "@tsexample\n"
 175   "// Inform GuiMLTextCtrl object to scroll to its top\n"
 176   "%thisGuiMLTextCtrl.scrollToTop();\n"
 177   "@endtsexample\n\n"
 178   "@see GuiControl")
 179{
 180   object->scrollToTop();
 181}
 182
 183DefineEngineMethod( GuiMLTextCtrl, scrollToBottom, void, (),,
 184   "@brief Scroll to the bottom of the text.\n\n"
 185   "@tsexample\n"
 186   "// Inform GuiMLTextCtrl object to scroll to its bottom\n"
 187   "%thisGuiMLTextCtrl.scrollToBottom();\n"
 188   "@endtsexample\n\n"
 189   "@see GuiControl")
 190{
 191   object->scrollToBottom();
 192}
 193
 194DefineEngineFunction(StripMLControlChars, const char*, (const char* inString),,
 195                "@brief Strip TorqueML control characters from the specified string, returning a 'clean' version.\n\n"
 196                "@param inString String to strip TorqueML control characters from.\n"
 197                "@tsexample\n"
 198                "// Define the string to strip TorqueML control characters from\n"
 199                "%string = \"<font:Arial:24>How Now <color:c43c12>Brown <color:000000>Cow\";\n\n"
 200                "// Request the stripped version of the string\n"
 201                "%strippedString = StripMLControlChars(%string);\n"
 202                "@endtsexample\n\n"
 203                "@return Version of the inputted string with all TorqueML characters removed.\n\n"
 204                "@see References\n\n"
 205                "@ingroup GuiCore")
 206{
 207   return GuiMLTextCtrl::stripControlChars(inString);
 208}
 209
 210DefineEngineMethod( GuiMLTextCtrl, forceReflow, void, (),,
 211   "@brief Forces the text control to reflow the text after new text is added, possibly resizing the control.\n\n"
 212   "@tsexample\n"
 213   "// Define new text to add\n"
 214   "%newText = \"BACON!\";\n\n"
 215   "// Add the new text to the control\n"
 216   "%thisGuiMLTextCtrl.addText(%newText);\n\n"
 217   "// Inform the GuiMLTextCtrl object to force a reflow to ensure the added text fits properly.\n"
 218   "%thisGuiMLTextCtrl.forceReflow();\n"
 219   "@endtsexample\n\n"
 220   "@see GuiControl")
 221{
 222   if(!object->isAwake())
 223      Con::errorf("GuiMLTextCtrl::forceReflow can only be called on visible controls.");
 224   else
 225      object->reflow();
 226}
 227
 228//--------------------------------------------------------------------------
 229GuiMLTextCtrl::GuiMLTextCtrl()
 230: mTabStops( NULL ), 
 231  mTabStopCount( 0 ), 
 232  mCurTabStop( 0 ), 
 233  mCurStyle( NULL ), 
 234  mCurLMargin( 0 ), 
 235  mCurRMargin( 100 ), 
 236  mCurJustify( LeftJustify ), 
 237  mCurDiv( 0 ),
 238  mCurY( 0 ), 
 239  mCurClipX( 0 ), 
 240  mLineAtoms( NULL ), 
 241  mLineList( NULL ),
 242  mLineAtomPtr( &mLineAtoms ), 
 243  mLineInsert( &mLineList ), 
 244  mScanPos( 0 ),
 245  mCurX( 0 ),
 246  mMaxY( 0 ),
 247  mCurURL( NULL ),
 248  mLineStart( 0 ),
 249  mVertMoveAnchor( 0 ),
 250  mVertMoveAnchorValid( false ),
 251  mIsEditCtrl( false ),
 252  mSelectionAnchor( 0 ),
 253  mCursorPosition( false ),
 254  mMaxBufferSize( -1 ),
 255  mInitialText( StringTable->EmptyString() ),
 256  mSelectionActive( false ),
 257  mSelectionStart( 0 ),
 258  mSelectionEnd( 0 ),
 259  mLineSpacingPixels( 2 ),
 260  mAllowColorChars( false ),
 261  mBitmapList( 0 ),
 262  mUseURLMouseCursor( false ),
 263  mBitmapRefList( 0 ),
 264  mTagList( NULL ),
 265  mDirty( true ), 
 266  mAlpha( 1.0f ),
 267  mHitURL( 0 ),
 268  mFontList( NULL )
 269{   
 270   mActive = true;
 271   //mInitialText = StringTable->EmptyString();
 272   Sim::findObject("InputDeniedSound", mDeniedSound);
 273}
 274
 275//--------------------------------------------------------------------------
 276GuiMLTextCtrl::~GuiMLTextCtrl()
 277{
 278   mCursorPosition     = 0;
 279
 280   mSelectionActive = false;
 281   mSelectionStart  = 0;
 282   mSelectionEnd    = 0;
 283   freeResources();
 284}
 285
 286//--------------------------------------------------------------------------
 287void GuiMLTextCtrl::initPersistFields()
 288{
 289   addGroup( "Text" );
 290   
 291      addField("lineSpacing",       TypeS32,    Offset(mLineSpacingPixels, GuiMLTextCtrl), "The number of blank pixels to place between each line.\n");
 292      addField("allowColorChars",   TypeBool,   Offset(mAllowColorChars,   GuiMLTextCtrl), "If true, the control will allow characters to have unique colors.");
 293      addField("maxChars",          TypeS32,    Offset(mMaxBufferSize,     GuiMLTextCtrl), "Maximum number of characters that the control will display.");
 294      addField("deniedSound",       TypeSFXTrackName, Offset(mDeniedSound, GuiMLTextCtrl), "If the text will not fit in the control, the deniedSound is played.");
 295      addField("text",              TypeCaseString,  Offset( mInitialText, GuiMLTextCtrl ), "Text to display in this control.");
 296      addField("useURLMouseCursor", TypeBool,   Offset(mUseURLMouseCursor,   GuiMLTextCtrl), "If true, the mouse cursor will turn into a hand cursor while over a link in the text.\n"
 297                                                                      "This is dependant on the markup language used by the GuiMLTextCtrl\n");
 298   
 299   endGroup( "Text" );
 300   
 301   Parent::initPersistFields();
 302}
 303
 304DefineEngineMethod( GuiMLTextCtrl, setAlpha, void, (F32 alphaVal),,
 305   "@brief Sets the alpha value of the control.\n\n"
 306   "@param alphaVal n - 1.0 floating value for the alpha\n"
 307   "@tsexample\n"
 308   "// Define the alphe value\n"
 309   "%alphaVal = \"0.5\";\n\n"
 310   "// Inform the control to update its alpha value.\n"
 311   "%thisGuiMLTextCtrl.setAlpha(%alphaVal);\n"
 312   "@endtsexample\n\n"
 313   "@see GuiControl")
 314{
 315   object->setAlpha(alphaVal);
 316}
 317
 318//--------------------------------------------------------------------------
 319
 320bool GuiMLTextCtrl::onAdd()
 321{
 322   if(!Parent::onAdd())
 323      return false;
 324
 325   if (!mTextBuffer.length() && mInitialText[0] != 0)
 326      setText(mInitialText, dStrlen(mInitialText)+1);
 327   return true;
 328}
 329
 330//--------------------------------------------------------------------------
 331bool GuiMLTextCtrl::onWake()
 332{
 333   if (Parent::onWake() == false)
 334      return false;
 335
 336   mDirty = true;
 337   return true;
 338}
 339
 340//--------------------------------------------------------------------------
 341void GuiMLTextCtrl::onPreRender()
 342{
 343   if(mDirty)
 344      reflow();
 345}
 346
 347//--------------------------------------------------------------------------
 348void GuiMLTextCtrl::drawAtomText(bool sel, U32 start, U32 end, Atom *atom, Line *line, Point2I offset)
 349{
 350   GFont *font = atom->style->font->fontRes;
 351   U32 xOff = 0;
 352   if(start != atom->textStart)
 353   {
 354      const UTF16* buff = mTextBuffer.getPtr() + atom->textStart;
 355      xOff += font->getStrNWidth(buff, start - atom->textStart);
 356   }
 357
 358   Point2I drawPoint(offset.x + atom->xStart + xOff, offset.y + atom->yStart);
 359
 360   ColorI color;
 361   if(atom->url)
 362   { 
 363      if(atom->url->mouseDown)
 364         color = atom->style->linkColorHL;
 365      else
 366         color = atom->style->linkColor;
 367   }
 368   else
 369      color = atom->style->color;
 370            
 371   const UTF16* tmp = mTextBuffer.getPtr() + start;
 372   U32 tmpLen = end-start;
 373
 374   GFXDrawUtil *drawer = GFX->getDrawUtil();
 375
 376   if(!sel)
 377   {
 378      if(atom->style->shadowOffset.x || atom->style->shadowOffset.y)
 379      {
 380         ColorI shadowColor = atom->style->shadowColor;
 381         shadowColor.alpha = (S32)(mAlpha * shadowColor.alpha);
 382         drawer->setBitmapModulation(shadowColor);
 383         drawer->drawTextN(font, drawPoint + atom->style->shadowOffset, 
 384            tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
 385      }
 386
 387      color.alpha = (S32)(mAlpha * color.alpha);
 388      drawer->setBitmapModulation(color);
 389      drawer->drawTextN(font, drawPoint, tmp, end-start, mAllowColorChars ? mProfile->mFontColors : NULL);
 390
 391      //if the atom was "clipped", see if we need to draw a "..." at the end
 392      if (atom->isClipped)
 393      {
 394         Point2I p2 = drawPoint;
 395         p2.x += font->getStrNWidthPrecise(tmp, tmpLen);
 396         drawer->drawTextN(font, p2, "...", 3, mAllowColorChars ? mProfile->mFontColors : NULL);
 397      }
 398   }
 399   else
 400   {
 401      RectI rect;
 402      rect.point.x = drawPoint.x;
 403      rect.point.y = line->y + offset.y;
 404      rect.extent.x = font->getStrNWidth(tmp, tmpLen) + 1;
 405      rect.extent.y = line->height + 1;
 406      
 407      drawer->drawRectFill(rect, mProfile->mFillColorHL);
 408      drawer->setBitmapModulation( mProfile->mFontColorHL );  // over-ride atom color:
 409      drawer->drawTextN(font, drawPoint, tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
 410
 411      //if the atom was "clipped", see if we need to draw a "..." at the end
 412      if (atom->isClipped)
 413      {
 414         Point2I p2 = drawPoint;
 415         p2.x += font->getStrNWidthPrecise(tmp, end - atom->textStart);
 416         drawer->drawTextN(font, p2, "...", 3, mAllowColorChars ? mProfile->mFontColors : NULL);
 417      }
 418   }
 419
 420   if(atom->url && !atom->url->noUnderline)
 421   {
 422      drawPoint.y += atom->baseLine + 2;
 423      Point2I p2 = drawPoint;
 424      p2.x += font->getStrNWidthPrecise(tmp, end - atom->textStart);
 425      drawer->drawLine(drawPoint, p2, color);
 426   }
 427}
 428
 429//--------------------------------------------------------------------------
 430void GuiMLTextCtrl::onRender(Point2I offset, const RectI& updateRect)
 431{
 432   Parent::onRender(offset, updateRect);
 433
 434   GFXDrawUtil *drawer = GFX->getDrawUtil();
 435
 436   // draw all the bitmaps
 437   for(BitmapRef *walk = mBitmapRefList; walk; walk = walk->next)
 438   {
 439      RectI screenBounds = *walk;
 440      screenBounds.point += offset;
 441      if(!screenBounds.overlaps(updateRect))
 442         continue;
 443
 444      drawer->clearBitmapModulation();
 445      drawer->drawBitmap(walk->bitmap->bitmapObject, screenBounds.point);
 446      //GFX->drawRectFill(screenBounds, mProfile->mFillColor);
 447   }
 448
 449   offset += mProfile->mTextOffset;
 450
 451   // draw all the text and dividerStyles
 452   for(Line *lwalk = mLineList; lwalk; lwalk = lwalk->next)
 453   {
 454      RectI lineRect(offset.x, offset.y + lwalk->y, getWidth(), lwalk->height);
 455
 456      if(!lineRect.overlaps(updateRect))
 457         continue;
 458
 459      if(lwalk->divStyle)
 460         drawer->drawRectFill(lineRect, mProfile->mFillColorHL);
 461
 462      for(Atom *awalk = lwalk->atomList; awalk; awalk = awalk->next)
 463      {
 464         if(!mSelectionActive || mSelectionEnd < awalk->textStart || mSelectionStart >= awalk->textStart + awalk->len)
 465            drawAtomText(false, awalk->textStart, awalk->textStart + awalk->len, awalk, lwalk, offset);
 466         else
 467         {
 468            U32 selectionStart = getMax(awalk->textStart, mSelectionStart);
 469            U32 selectionEnd = getMin(awalk->textStart + awalk->len, mSelectionEnd + 1);
 470
 471            // draw some unselected text
 472            if(selectionStart > awalk->textStart)
 473               drawAtomText(false, awalk->textStart, selectionStart, awalk, lwalk, offset);
 474
 475            // draw the selection
 476            drawAtomText(true, selectionStart, selectionEnd, awalk, lwalk, offset);
 477
 478            if(selectionEnd < awalk->textStart + awalk->len)
 479               drawAtomText(false, selectionEnd, awalk->textStart + awalk->len, awalk, lwalk, offset);
 480         }
 481      }
 482   }
 483
 484   drawer->clearBitmapModulation();
 485}
 486
 487//--------------------------------------------------------------------------
 488void GuiMLTextCtrl::freeLineBuffers()
 489{
 490   mViewChunker.freeBlocks();
 491   mLineList = NULL;
 492   mBitmapRefList = NULL;
 493   mTagList = NULL;
 494   mHitURL = 0;
 495   mDirty = true;
 496}
 497
 498//--------------------------------------------------------------------------
 499void GuiMLTextCtrl::freeResources()
 500{
 501   for(Font* walk = mFontList; walk; walk = walk->next)
 502   {
 503      walk->fontRes = NULL;
 504      delete[] walk->faceName;
 505   }
 506
 507   for(Bitmap* bwalk = mBitmapList; bwalk; bwalk = bwalk->next)
 508      bwalk->bitmapObject = 0;
 509
 510   mFontList = NULL;
 511   mBitmapList = NULL;
 512   mResourceChunker.freeBlocks();
 513   mDirty = true;
 514
 515   freeLineBuffers();
 516}
 517
 518//--------------------------------------------------------------------------
 519void GuiMLTextCtrl::onSleep()
 520{
 521   freeResources();
 522   Parent::onSleep();
 523}
 524
 525//--------------------------------------------------------------------------
 526void GuiMLTextCtrl::inspectPostApply()
 527{
 528   Parent::inspectPostApply();
 529
 530   setText(mInitialText, dStrlen(mInitialText));
 531
 532   if (mLineSpacingPixels < 0)
 533      mLineSpacingPixels = 0;
 534}
 535
 536
 537//--------------------------------------------------------------------------
 538void GuiMLTextCtrl::parentResized(const RectI& oldParentRect, const RectI& newParentRect)
 539{
 540   Parent::parentResized(oldParentRect, newParentRect);
 541   mDirty = true;
 542}
 543
 544//--------------------------------------------------------------------------
 545U32 GuiMLTextCtrl::getNumChars() const
 546{
 547   return mTextBuffer.length();
 548}
 549
 550//--------------------------------------------------------------------------
 551U32 GuiMLTextCtrl::getText(char* pBuffer, const U32 bufferSize) const
 552{
 553   mTextBuffer.getCopy8(pBuffer, bufferSize);
 554
 555   return getMin(mTextBuffer.length(), bufferSize);
 556}
 557
 558//--------------------------------------------------------------------------
 559const char* GuiMLTextCtrl::getTextContent()
 560{
 561   if ( mTextBuffer.length() > 0 )
 562   {
 563      UTF8* returnString = Con::getReturnBuffer( mTextBuffer.getUTF8BufferSizeEstimate() );
 564      mTextBuffer.getCopy8(returnString, mTextBuffer.getUTF8BufferSizeEstimate() );
 565      return returnString;
 566   }
 567
 568   return( "" );
 569}
 570
 571//--------------------------------------------------------------------------
 572const char *GuiMLTextCtrl::getScriptValue()
 573{
 574   return getTextContent();
 575}
 576
 577//--------------------------------------------------------------------------
 578void GuiMLTextCtrl::setScriptValue(const char *newText)
 579{
 580   setText(newText, dStrlen(newText));
 581}
 582
 583//--------------------------------------------------------------------------
 584void GuiMLTextCtrl::setText(const char* textBuffer, const U32 numChars)
 585{
 586   U32 chars = numChars;
 587   if(numChars >= mMaxBufferSize)
 588      chars = mMaxBufferSize;
 589
 590   // leaving this usage because we StringBuffer.set((UTF8*)) cannot take a numChars arg.
 591   // perhaps it should? -paxorr
 592   FrameTemp<UTF8> tmp(chars+1);
 593   dStrncpy(tmp, textBuffer, chars);
 594   tmp[chars] = 0;
 595
 596   mTextBuffer.set(tmp);
 597
 598   //after setting text, always set the cursor to the beginning
 599   setCursorPosition(0);
 600   clearSelection();
 601   mDirty = true;
 602   scrollToTop();
 603}
 604
 605//--------------------------------------------------------------------------
 606void GuiMLTextCtrl::addText(const char* textBuffer, const U32 numChars, bool reformat)
 607{
 608   if(numChars >= mMaxBufferSize)
 609      return;
 610
 611   FrameTemp<UTF8> tmp(numChars+1);
 612   dStrncpy(tmp, textBuffer, numChars);
 613   tmp[numChars] = 0;
 614
 615   mTextBuffer.append(tmp);
 616
 617   //after setting text, always set the cursor to the beginning
 618   if (reformat)
 619   {
 620      setCursorPosition(0);
 621      clearSelection();
 622      mDirty = true;
 623      scrollToTop();
 624   }
 625}
 626
 627//--------------------------------------------------------------------------
 628bool GuiMLTextCtrl::setCursorPosition(const S32 newPosition)
 629{
 630   if (newPosition < 0) 
 631   {
 632      mCursorPosition = 0;
 633      return true;
 634   }
 635   else if (newPosition >= mTextBuffer.length() - 1)
 636   {
 637      mCursorPosition = mTextBuffer.length();
 638      return true;
 639   }
 640   else 
 641   {
 642      mCursorPosition = newPosition;
 643      return false;
 644   }
 645}
 646
 647//--------------------------------------------------------------------------
 648void GuiMLTextCtrl::ensureCursorOnScreen()
 649{
 650   // If our parent isn't a scroll control, or we're not the only control
 651   //  in the content region, bail...
 652   GuiControl* pParent = getParent();
 653   GuiScrollCtrl *sc = dynamic_cast<GuiScrollCtrl*>(pParent);
 654   if(!sc)
 655      return;
 656
 657   // Ok.  Now we know that our parent is a scroll control.  Let's find the
 658   //  top of the cursor, and it's bottom.  We can then scroll the parent control
 659   //  if appropriate...
 660
 661   Point2I cursorTopP, cursorBottomP;
 662   ColorI color;
 663   getCursorPositionAndColor(cursorTopP, cursorBottomP, color);
 664
 665   sc->scrollRectVisible(RectI(cursorTopP.x, cursorTopP.y, 1, cursorBottomP.y - cursorTopP.y));
 666}
 667
 668//--------------------------------------
 669void GuiMLTextCtrl::getCursorPositionAndColor(Point2I &cursorTop, Point2I &cursorBottom, ColorI &color)
 670{
 671   S32 x = 0;
 672   S32 y = 0;
 673   S32 height = (mProfile && mProfile->mFont) ? mProfile->mFont->getHeight() : 0;
 674   color = mProfile->mCursorColor;
 675   for(Line *walk = mLineList; walk; walk = walk->next)
 676   {
 677      if ((mCursorPosition < walk->textStart + walk->len) || (walk->next == NULL))
 678      {
 679         // it's in the atoms on this line...
 680         y = walk->y;
 681         height = walk->height;
 682
 683         for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next)
 684         {
 685
 686            if(mCursorPosition < awalk->textStart)
 687            {
 688               x = awalk->xStart;
 689               goto done;
 690            }
 691
 692            if(mCursorPosition > awalk->textStart + awalk->len)
 693            {
 694               x = awalk->xStart + awalk->width;
 695               continue;
 696            }
 697
 698            // it's in the text block...
 699            x = awalk->xStart;
 700            GFont *font = awalk->style->font->fontRes;
 701
 702            const UTF16* buff = mTextBuffer.getPtr() + awalk->textStart;
 703            x += font->getStrNWidth(buff, mCursorPosition - awalk->textStart);// - 1);
 704
 705            color = awalk->style->color;
 706            goto done;
 707         }
 708
 709         //if it's within this walk's width, but we didn't find an atom, leave the cursor at the beginning of the line...
 710         goto done;
 711      }
 712   }
 713done:
 714   cursorTop.set(x, y);
 715   cursorBottom.set(x, y + height);
 716}
 717
 718//--------------------------------------------------------------------------
 719// Keyboard events...
 720bool GuiMLTextCtrl::onKeyDown(const GuiEvent& event)
 721{
 722   //only cut/copy work with this control...
 723   if (event.modifier & SI_COPYPASTE)
 724   {
 725      switch(event.keyCode)
 726      {
 727         //copy
 728         case KEY_C:
 729         {
 730            //make sure we actually have something selected
 731            if (mSelectionActive)
 732            {
 733               copyToClipboard(mSelectionStart, mSelectionEnd);
 734               setUpdate();
 735            }
 736            return true;
 737         }
 738         
 739         default:
 740            break;
 741      }
 742   }
 743
 744   // Otherwise, let the parent have the event...
 745   return Parent::onKeyDown(event);
 746}
 747
 748//--------------------------------------------------------------------------
 749// Mousing events...
 750void GuiMLTextCtrl::onMouseDown(const GuiEvent& event)
 751{
 752   if(!mActive)
 753      return;
 754
 755   Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint));
 756   if(hitAtom && !mIsEditCtrl)
 757   {
 758      mouseLock();
 759      mHitURL = hitAtom->url;
 760      if (mHitURL)
 761         mHitURL->mouseDown = true;
 762   }
 763
 764   setFirstResponder();
 765   mouseLock();
 766
 767   mSelectionActive = false;
 768   mSelectionAnchor        = getTextPosition(globalToLocalCoord(event.mousePoint));
 769   mSelectionAnchorDropped = event.mousePoint;
 770   if (mSelectionAnchor < 0)
 771      mSelectionAnchor = 0;
 772   else if (mSelectionAnchor >= mTextBuffer.length() - 1)
 773      mSelectionAnchor = mTextBuffer.length();
 774
 775   mVertMoveAnchorValid = false;
 776
 777   setUpdate();
 778}
 779
 780//--------------------------------------------------------------------------
 781void GuiMLTextCtrl::onMouseDragged(const GuiEvent& event)
 782{
 783   if (!mActive || (getRoot()->getMouseLockedControl() != this))
 784      return;
 785
 786   Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint));
 787   bool down = false;
 788
 789   //note mHitURL can't be set unless this is (!mIsEditCtrl)
 790   if(hitAtom && hitAtom->url == mHitURL)
 791      down = true;
 792
 793   if(mHitURL && down != mHitURL->mouseDown)
 794      mHitURL->mouseDown = down;
 795
 796   if (!mHitURL)
 797   {
 798      S32 newSelection = 0;
 799      newSelection = getTextPosition(globalToLocalCoord(event.mousePoint));
 800      if (newSelection < 0)
 801         newSelection = 0;
 802      else if (newSelection > mTextBuffer.length())
 803         newSelection = mTextBuffer.length();
 804
 805      if (newSelection == mSelectionAnchor) 
 806      {
 807         mSelectionActive = false;
 808      }
 809      else if (newSelection > mSelectionAnchor) 
 810      {
 811         mSelectionActive = true;
 812         mSelectionStart  = mSelectionAnchor;
 813         mSelectionEnd    = newSelection - 1;
 814      }
 815      else 
 816      {
 817         mSelectionStart  = newSelection;
 818         mSelectionEnd    = mSelectionAnchor - 1;
 819         mSelectionActive = true;
 820      }
 821      setCursorPosition(newSelection);
 822      mDirty = true;
 823   }
 824
 825   setUpdate();
 826}
 827
 828//--------------------------------------------------------------------------
 829void GuiMLTextCtrl::onMouseUp(const GuiEvent& event)
 830{
 831   if (!mActive || (getRoot()->getMouseLockedControl() != this))
 832      return;
 833
 834   mouseUnlock();
 835
 836   //see if we've clicked on a URL
 837   Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint));
 838   if (mHitURL && hitAtom && hitAtom->url == mHitURL && mHitURL->mouseDown)
 839   {
 840      mHitURL->mouseDown = false;
 841
 842      // Convert URL from UTF16 to UTF8.
 843      UTF8* url = mTextBuffer.createSubstring8(mHitURL->textStart, mHitURL->len);
 844     onURL_callback(url);
 845
 846      delete[] url;
 847      mHitURL = NULL;
 848
 849      setUpdate();
 850      return;
 851   }
 852
 853   //else, update our selection
 854   else
 855   {
 856      if ((event.mousePoint - mSelectionAnchorDropped).len() < 3)
 857         mSelectionActive = false;
 858
 859      setCursorPosition(getTextPosition(globalToLocalCoord(event.mousePoint)));
 860      mVertMoveAnchorValid = false;
 861      setUpdate();
 862   }
 863}
 864
 865//--------------------------------------------------------------------------
 866void GuiMLTextCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
 867{
 868   if(!mUseURLMouseCursor)
 869   {
 870      Parent::getCursor(cursor, showCursor, lastGuiEvent);
 871      return;
 872   }
 873
 874   GuiCanvas *pRoot = getRoot();
 875   if( !pRoot )
 876      return;
 877
 878   PlatformWindow *pWindow = pRoot->getPlatformWindow();
 879   AssertFatal(pWindow != NULL,"GuiControl without owning platform window!  This should not be possible.");
 880   PlatformCursorController *pController = pWindow->getCursorController();
 881   AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
 882
 883   Atom *hitAtom = findHitAtom(globalToLocalCoord(lastGuiEvent.mousePoint));
 884   if(hitAtom && !mIsEditCtrl && hitAtom->url)
 885   {
 886      if(pRoot->mCursorChanged != PlatformCursorController::curHand)
 887      {
 888         // We've already changed the cursor, so set it back before we change it again.
 889         if(pRoot->mCursorChanged != -1)
 890            pController->popCursor();
 891
 892         // Now change the cursor shape
 893         pController->pushCursor(PlatformCursorController::curHand);
 894         pRoot->mCursorChanged = PlatformCursorController::curHand;
 895
 896      }
 897   }
 898   else if(pRoot->mCursorChanged != -1)
 899   {
 900         // Restore the cursor we changed
 901         pController->popCursor();
 902         pRoot->mCursorChanged = -1;
 903   }
 904}
 905
 906//--------------------------------------------------------------------------
 907void GuiMLTextCtrl::insertChars(const char* inputChars,
 908                                const U32   numInputChars,
 909                                const U32   position)
 910{
 911   AssertFatal(isSelectionActive() == false, "GuiMLTextCtrl::insertChars: don't use this function when there's a selection active!");
 912   AssertFatal(position <= mTextBuffer.length(), "GuiMLTextCtrl::insertChars: can't insert outside of current text!");
 913
 914   //make sure the text will fit...
 915   S32 numCharsToInsert = numInputChars;
 916   if (mMaxBufferSize > 0 && mTextBuffer.length() + numInputChars + 1 > mMaxBufferSize)
 917      numCharsToInsert = mMaxBufferSize - mTextBuffer.length() - 1;
 918   if (numCharsToInsert <= 0)
 919   {
 920      // Play the "Denied" sound:
 921      if ( numInputChars > 0 && mDeniedSound )
 922         SFX->playOnce(mDeniedSound);
 923
 924      return;
 925   }
 926
 927   mTextBuffer.insert(position, inputChars );
 928
 929   if (mCursorPosition >= position) 
 930   {
 931      // Cursor was at or after the inserted text, move forward...
 932      mCursorPosition += numCharsToInsert;
 933   }
 934
 935   AssertFatal(mCursorPosition <= mTextBuffer.length(), "GuiMLTextCtrl::insertChars: bad cursor position");
 936   mDirty = true;
 937}
 938
 939//--------------------------------------------------------------------------
 940void GuiMLTextCtrl::deleteChars(const U32 rangeStart,
 941                                const U32 rangeEnd)
 942{
 943   AssertFatal(isSelectionActive() == false, "GuiMLTextCtrl::deleteChars: don't use this function when there's a selection active");
 944   AssertFatal(rangeStart <= mTextBuffer.length() && rangeEnd <= mTextBuffer.length(),
 945               avar("GuiMLTextCtrl::deleteChars: can't delete outside of current text (%d, %d, %d)",
 946                    rangeStart, rangeEnd, mTextBuffer.length()));
 947   AssertFatal(rangeStart <= rangeEnd, "GuiMLTextCtrl::deleteChars: invalid delete range");
 948
 949   // Currently deleting text doesn't resize the text buffer, perhaps this should
 950   //  change?
 951   mTextBuffer.cut(rangeStart, rangeEnd - rangeStart);
 952
 953   if (mCursorPosition <= rangeStart) 
 954   {
 955      // Cursor placed before deleted text, ignore
 956   }
 957   else if (mCursorPosition > rangeStart && mCursorPosition <= rangeEnd) 
 958   {
 959      // Cursor inside deleted text, set to start of range
 960      mCursorPosition = rangeStart;
 961   }
 962   else 
 963   {
 964      // Cursor after deleted text, decrement by number of chars deleted
 965      mCursorPosition -= (rangeEnd - rangeStart) + 1;
 966   }
 967
 968   AssertFatal(mCursorPosition <= mTextBuffer.length(), "GuiMLTextCtrl::deleteChars: bad cursor position");
 969   mDirty = true;
 970}
 971
 972//--------------------------------------------------------------------------
 973void GuiMLTextCtrl::copyToClipboard(const U32 rangeStart, const U32 rangeEnd)
 974{
 975   AssertFatal(rangeStart < mTextBuffer.length() && rangeEnd < mTextBuffer.length(),
 976               avar("GuiMLTextCtrl::copyToClipboard: can't copy outside of current text (%d, %d, %d)",
 977                    rangeStart, rangeEnd, mTextBuffer.length()));
 978   AssertFatal(rangeStart <= rangeEnd, "GuiMLTextCtrl::copyToClipboard: invalid copy range");
 979
 980   //copy the selection to the clipboard
 981
 982   UTF8* selection = mTextBuffer.createSubstring8(rangeStart, rangeEnd-rangeStart+1);
 983   Platform::setClipboard(selection);
 984   delete[] selection;
 985}
 986
 987//--------------------------------------------------------------------------
 988bool GuiMLTextCtrl::isSelectionActive() const
 989{
 990   return mSelectionActive;
 991}
 992
 993//--------------------------------------------------------------------------
 994void GuiMLTextCtrl::clearSelection()
 995{
 996   mSelectionActive = false;
 997   mSelectionStart  = 0;
 998   mSelectionEnd    = 0;
 999}
1000
1001//--------------------------------------------------------------------------
1002void GuiMLTextCtrl::scrollToTag( U32 id )
1003{
1004   // If the parent control is not a GuiScrollContentCtrl, then this call is invalid:
1005   GuiScrollCtrl *pappy = dynamic_cast<GuiScrollCtrl*>(getParent());
1006   if ( !pappy )
1007      return;
1008
1009   // Find the indicated tag:
1010   LineTag* tag = NULL;
1011   for ( tag = mTagList; tag; tag = tag->next )
1012   {
1013      if ( tag->id == id )
1014         break;
1015   }
1016
1017   if ( !tag )
1018   {
1019      Con::warnf( ConsoleLogEntry::General, "GuiMLTextCtrl::scrollToTag - tag id %d not found!", id );
1020      return;
1021   }
1022   pappy->scrollRectVisible(RectI(0, tag->y, 1, 1));
1023}
1024
1025//--------------------------------------------------------------------------
1026void GuiMLTextCtrl::scrollToTop()
1027{
1028   // If the parent control is not a GuiScrollContentCtrl, then this call is invalid:
1029   GuiScrollCtrl *pappy = dynamic_cast<GuiScrollCtrl*>(getParent());
1030   if ( !pappy )
1031      return;
1032   pappy->scrollRectVisible(RectI(0,0,0,0));
1033}
1034
1035//--------------------------------------------------------------------------
1036void GuiMLTextCtrl::scrollToBottom()
1037{
1038   // If the parent control is not a GuiScrollContentCtrl, then this call is invalid:
1039   GuiScrollCtrl *pappy = dynamic_cast<GuiScrollCtrl*>(getParent());
1040   if ( !pappy )
1041      return;
1042
1043   // Figure bounds for the bottom left corner
1044   RectI cornerBounds (0, getPosition().y + getExtent().y, 1, 1);
1045   pappy->scrollRectVisible(cornerBounds);
1046}
1047
1048//--------------------------------------------------------------------------
1049GuiMLTextCtrl::Atom *GuiMLTextCtrl::findHitAtom(const Point2I localCoords)
1050{
1051   AssertFatal(mAwake, "Can't get the text position of a sleeping control.");
1052   if(mDirty)
1053      reflow();
1054   for(Line *walk = mLineList; walk; walk = walk->next)
1055   {
1056      if(localCoords.y < walk->y)
1057         return NULL;
1058
1059      if(localCoords.y >= walk->y && localCoords.y < walk->y + walk->height)
1060      {
1061         for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next)
1062         {
1063            if(localCoords.x < awalk->xStart)
1064               return NULL;
1065            if(localCoords.x >= awalk->xStart + awalk->width)
1066               continue;
1067            return awalk;
1068         }
1069      }
1070   }
1071   return NULL;
1072}
1073
1074//--------------------------------------------------------------------------
1075S32 GuiMLTextCtrl::getTextPosition(const Point2I& localCoords)
1076{
1077   AssertFatal(mAwake, "Can't get the text position of a sleeping control.");
1078   if(mDirty)
1079      reflow();
1080
1081   for(Line *walk = mLineList; walk; walk = walk->next)
1082   {
1083      if((S32)localCoords.y < (S32)walk->y)
1084         return walk->textStart;
1085
1086      if(localCoords.y >= walk->y && localCoords.y < walk->y + walk->height)
1087      {
1088         for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next)
1089         {
1090            if(localCoords.x < awalk->xStart)
1091               return awalk->textStart;
1092            if(localCoords.x >= awalk->xStart + awalk->width)
1093               continue;
1094            // it's in the text block...
1095            GFont *font = awalk->style->font->fontRes;
1096
1097            const UTF16 *tmp16 = mTextBuffer.getPtr() + awalk->textStart;
1098            U32 bp = font->getBreakPos(tmp16, awalk->len, localCoords.x - awalk->xStart, false);
1099            return awalk->textStart + bp;
1100         }
1101         return walk->textStart + walk->len;
1102      }
1103   }
1104   return mTextBuffer.length() - 1;
1105}
1106
1107//--------------------------------------------------------------------------
1108GuiMLTextCtrl::Font *GuiMLTextCtrl::allocFont(const char *faceName, U32 faceNameLen, U32 size)
1109{
1110   // check if it's in the font list currently:
1111   for(Font *walk = mFontList; walk; walk = walk->next)
1112      if(faceNameLen == walk->faceNameLen &&
1113         !dStrncmp(walk->faceName, faceName, faceNameLen) &&
1114         size == walk->size)
1115         return walk;
1116
1117   // Create!
1118   Font *ret;
1119   ret = constructInPlace((Font *) mResourceChunker.alloc(sizeof(Font)));
1120   ret->faceName = new char[faceNameLen+1];
1121   dStrncpy(ret->faceName, faceName, faceNameLen);
1122   ret->faceName[faceNameLen] = '\0';
1123   ret->faceNameLen = faceNameLen;
1124   ret->size = size;
1125   ret->next = mFontList;
1126   ret->fontRes = GFont::create(ret->faceName, size, GuiControlProfile::sFontCacheDirectory);
1127   if(ret->fontRes != NULL)
1128   {
1129      ret->next = mFontList;
1130      mFontList = ret;
1131      return ret;
1132   }
1133   return NULL;
1134}
1135
1136//--------------------------------------------------------------------------
1137GuiMLTextCtrl::Bitmap *GuiMLTextCtrl::allocBitmap(const char *bitmapName, U32 bitmapNameLen)
1138{
1139   for(Bitmap *walk = mBitmapList; walk; walk = walk->next)
1140      if(bitmapNameLen == walk->bitmapNameLen &&
1141         !dStrncmp(walk->bitmapName, bitmapName, bitmapNameLen))
1142         return walk;
1143
1144   Bitmap *ret = constructInPlace((Bitmap *) mResourceChunker.alloc(sizeof(Bitmap)));
1145   const U32 BitmapNameSize = sizeof(ret->bitmapName);
1146   dStrncpy(ret->bitmapName, bitmapName, getMin(bitmapNameLen,BitmapNameSize));
1147   if (bitmapNameLen < BitmapNameSize)
1148      ret->bitmapName[bitmapNameLen] = 0;
1149   else
1150      ret->bitmapName[BitmapNameSize - 1] = 0;
1151   ret->bitmapNameLen = bitmapNameLen;
1152      
1153   ret->bitmapObject.set(ret->bitmapName, &GFXMLTextureProfile, avar("%s() - ret->bitmapObject (line %d)", __FUNCTION__, __LINE__));
1154   //ret->bitmapObject.set(bitmapName, &GFXMLTextureProfile);
1155   if( bool(ret->bitmapObject) )
1156   {
1157      ret->next = mBitmapList;
1158      mBitmapList = ret;
1159      return ret;
1160   }
1161   return NULL;
1162}
1163
1164//--------------------------------------------------------------------------
1165GuiMLTextCtrl::LineTag *GuiMLTextCtrl::allocLineTag(U32 id)
1166{
1167   for ( LineTag* walk = mTagList; walk; walk = walk->next )
1168   {
1169      if ( walk->id == id )
1170      {
1171         Con::warnf( ConsoleLogEntry::General, "GuiMLTextCtrl - can't add duplicate line tags!" );
1172         return( NULL );
1173      }
1174   }
1175   LineTag* newTag = (LineTag*) mViewChunker.alloc( sizeof( LineTag ) );
1176   newTag->id = id;
1177   newTag->y = mCurY;
1178   newTag->next = mTagList;
1179   mTagList = newTag;
1180
1181   return( newTag );
1182}
1183
1184//--------------------------------------------------------------------------
1185void GuiMLTextCtrl::emitNewLine(U32 textStart)
1186{
1187   //clear any clipping
1188   mCurClipX = 0;
1189
1190   Line *l = (Line *) mViewChunker.alloc(sizeof(Line));
1191   l->height = mCurStyle->font->fontRes->getHeight();
1192   l->y = mCurY;
1193   l->textStart = mLineStart;
1194   l->len = textStart - l->textStart;
1195   mLineStart = textStart;
1196   l->atomList = mLineAtoms;
1197   l->next = 0;
1198   l->divStyle = mCurDiv;
1199   *mLineInsert = l;
1200   mLineInsert = &(l->next);
1201   mCurX = mCurLMargin;
1202   mCurTabStop = 0;
1203
1204   if(mLineAtoms)
1205   {
1206      // scan through the atoms in the line, get the largest height
1207      U32 maxBaseLine = 0;
1208      U32 maxDescent = 0;
1209      Atom* walk;
1210
1211      for(walk = mLineAtoms; walk; walk = walk->next)
1212      {
1213         if(walk->baseLine > maxBaseLine)
1214            maxBaseLine = walk->baseLine;
1215         if(walk->descent > maxDescent)
1216            maxDescent = walk->descent;
1217         if(!walk->next)
1218         {
1219            l->len = walk->textStart + walk->len - l->textStart;
1220            mLineStart = walk->textStart + walk->len;
1221         }
1222      }
1223      l->height = maxBaseLine + maxDescent;
1224
1225      for(walk = mLineAtoms; walk; walk = walk->next)
1226         walk->yStart = mCurY + maxBaseLine - walk->baseLine;
1227   }
1228   mCurY += l->height;
1229   mLineAtoms = NULL;
1230   mLineAtomPtr = &mLineAtoms;
1231
1232   // clear out the blocker list
1233   BitmapRef **blockList = &mBlockList;
1234   while(*blockList)
1235   {
1236      BitmapRef *blk = *blockList;
1237      if(blk->point.y + blk->extent.y <= mCurY)
1238         *blockList = blk->nextBlocker;
1239      else
1240         blockList = &(blk->nextBlocker);
1241   }
1242   if(mCurY > mMaxY)
1243      mMaxY = mCurY;
1244}
1245
1246//--------------------------------------------------------------------------
1247void GuiMLTextCtrl::emitBitmapToken(GuiMLTextCtrl::Bitmap *bmp, U32 textStart, bool bitmapBreak)
1248{
1249   if(mCurRMargin <= mCurLMargin)
1250      return;
1251   if(mCurRMargin - mCurLMargin < bmp->bitmapObject->getWidth())
1252      return;
1253
1254   BitmapRef *ref = (BitmapRef *) mViewChunker.alloc(sizeof(BitmapRef));
1255   ref->bitmap = bmp;
1256   ref->next = mBitmapRefList;
1257   mBitmapRefList = ref;
1258
1259   // now we gotta insert it into the blocker list and figure out where it's spos to go...
1260   ref->extent.x = bmp->bitmapObject->getBitmapWidth();
1261   ref->extent.y = bmp->bitmapObject->getBitmapHeight();
1262
1263   // find the first space in the blocker list that will fit this thats > curLMargin
1264
1265   while(bitmapBreak && mBlockList != &mSentinel)
1266      emitNewLine(textStart);
1267
1268   for(;;)
1269   {
1270      // loop til we find a line that fits...
1271      // we'll have to emitLine repeatedly to clear out the block lists...
1272
1273      BitmapRef **walk = &mBlockList;
1274      U32 minx = mCurX;
1275      U32 maxx = mCurRMargin;
1276
1277      while(*walk)
1278      {
1279         BitmapRef *blk = *walk;
1280
1281         if(blk->point.x > minx)
1282         {
1283            U32 right = maxx;
1284            if(blk->point.x < right)
1285               right = blk->point.x;
1286            U32 width = right - minx;
1287
1288            if(right > minx && width >= ref->extent.x) // we've found the spot...
1289            {
1290               // insert it:
1291               U32 x = minx;
1292               if(mCurJustify == CenterJustify)
1293                  x += (width - ref->extent.x) >> 1;
1294               else if(mCurJustify == RightJustify)
1295                  x += width - ref->extent.x;
1296               ref->point.x = x;
1297               ref->point.y = mCurY;
1298               ref->nextBlocker = blk;
1299               *walk = ref;
1300               if(ref->point.y + ref->extent.y > mMaxY)
1301                  mMaxY = ref->point.y + ref->extent.y;
1302
1303               return;
1304            }
1305         }
1306         if(minx < blk->point.x + blk->extent.x)
1307            minx = blk->point.x + blk->extent.x;
1308         // move on to the next blocker...
1309         walk = &(blk->nextBlocker);
1310      }
1311      // go to the next line...
1312      emitNewLine(textStart);
1313   }
1314}
1315
1316//--------------------------------------------------------------------------
1317void GuiMLTextCtrl::emitTextToken(U32 textStart, U32 len)
1318{
1319   if(mCurRMargin <= mCurLMargin)
1320      return;
1321
1322   GFont *font = mCurStyle->font->fontRes;
1323   Atom *a = (Atom *) mViewChunker.alloc(sizeof(Atom));
1324   a->url = mCurURL;
1325
1326   a->style = mCurStyle;
1327   mCurStyle->used = true;
1328
1329   a->baseLine = font->getBaseline();
1330   a->descent = font->getDescent();
1331   a->textStart = textStart;
1332   a->len = len;
1333   a->isClipped = false;
1334   a->next = NULL;
1335   *mEmitAtomPtr = a;
1336   mEmitAtomPtr = &(a->next);
1337}
1338
1339//--------------------------------------------------------------------------
1340void GuiMLTextCtrl::processEmitAtoms()
1341{
1342   Atom *atomList = mEmitAtoms;
1343   mEmitAtoms = NULL;
1344   mEmitAtomPtr = &mEmitAtoms;
1345
1346   bool bailout = false;
1347
1348   while(atomList)
1349   {
1350      // split the tokenlist by space
1351      // first find the first space that the text can go into:
1352      BitmapRef *br = mBlockList;
1353      //bool bailout = false; // Scoping error here? Moved up one scope. -pw
1354      Atom *list = atomList;
1355
1356      while(br && atomList)
1357      {
1358         // if the blocker is before the current x, ignore it.
1359         if(br->point.x + br->extent.x <= mCurX)
1360         {
1361            br = br->nextBlocker;
1362            continue;
1363         }
1364         // if cur x is in the middle of the blocker
1365         // advance cur x to right edge of blocker.
1366         if(mCurX >= br->point.x)
1367         {
1368            mCurX = br->point.x + br->extent.x;
1369            br = br->nextBlocker;
1370            continue;
1371         }
1372         // get the remaining width
1373         U32 right = br->point.x;
1374         if(right > mCurRMargin)
1375            right = mCurRMargin;
1376
1377         //if we're clipping text, readjust
1378         if (mCurClipX > 0 && right > mCurClipX)
1379            right = mCurClipX;
1380
1381         // if there's no room, break to the next line...
1382         if(right <= mCurX)
1383            break;
1384
1385         // we've got some space:
1386         U32 width = right - mCurX;
1387         atomList = splitAtomListEmit(atomList, width);
1388         if(atomList) // there's more, so advance cur x
1389         {
1390            mCurX = br->point.x + br->extent.x;
1391            br = br->nextBlocker;
1392         }
1393      }
1394      if(mBlockList == &mSentinel && atomList == list)
1395      {
1396         if(bailout)
1397            return;
1398         else
1399            bailout = true;
1400      }
1401      // is there more to process for the next line?
1402      if(atomList)
1403         emitNewLine(mScanPos);
1404   }
1405}
1406
1407//--------------------------------------------------------------------------
1408GuiMLTextCtrl::Atom *GuiMLTextCtrl::splitAtomListEmit(Atom *list, U32 width)
1409{
1410   U32 totalWidth = 0;
1411   Atom *emitList = 0;
1412   Atom **emitPtr = &emitList;
1413
1414   bool adjustClipAtom = false;
1415   Atom *clipAtom = NULL;
1416   bool emitted = false;
1417
1418   while(list)
1419   {
1420      GFont *font = list->style->font->fontRes;
1421      U32 breakPos;
1422
1423      const UTF16 *tmp16 = mTextBuffer.getPtr() + list->textStart;
1424
1425      //if we're clipping the text, we don't break within an atom, we adjust the atom to only render
1426      //the portion of text that does fit, and to ignore the rest...
1427      if (mCurClipX > 0)
1428      {
1429         //find out how many character's fit within the given width
1430         breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth, false);
1431
1432         //if there isn't room for even the first character...
1433         if (breakPos == 0)
1434         {
1435            //set the atom's len and width to prevent it from being drawn
1436            list->len = 0;
1437            list->width = 0;
1438            adjustClipAtom = true;
1439         }
1440
1441         //if our text doesn't fit within the clip region, add a "..."
1442         else if (breakPos != list->len)
1443         {
1444            U32 etcWidth = font->getStrNWidthPrecise("...", 3);
1445            breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth - etcWidth, false);
1446
1447            //again, if there isn't even room for a single character before the "...."
1448            if (breakPos == 0)
1449            {
1450               //set the atom's len and width to prevent it from being drawn
1451               list->len = 0;
1452               list->width = 0;
1453               adjustClipAtom = true;
1454            }
1455            else
1456            {
1457               //set the char len to the break pos, and the rest of the characters in this atom will be ignored
1458               list->len = breakPos;
1459               list->width = width - totalWidth;
1460
1461               //mark this one as clipped
1462               list->isClipped = true;
1463               clipAtom = NULL;
1464            }
1465         }
1466      
1467         //otherwise no need to treat this atom any differently..
1468         else
1469         {
1470            //set the atom width == to the string length
1471            list->width = font->getStrNWidthPrecise(tmp16, breakPos);
1472
1473            //set the pointer to the last atom that fit within the clip region
1474            clipAtom = list;
1475         }
1476      }
1477      else
1478      {
1479         breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth, true);
1480         if(breakPos == 0 || (breakPos < list->len && mTextBuffer.getChar(list->textStart + breakPos - 1)!= ' ' && emitted))
1481            break;
1482
1483         //set the atom width == to the string length
1484         list->width = font->getStrNWidthPrecise(tmp16, breakPos);
1485      }
1486
1487      //update the total width
1488      totalWidth += list->width;
1489      
1490      // see if this is the last atom that will fit:
1491      Atom *emit = list;
1492      
1493      *emitPtr = emit;
1494      emitPtr = &(emit->next);
1495      emitted = true;
1496
1497      //if we're clipping, don't split the atom, otherwise, see if it needs to be split
1498      if(!list->isClipped && breakPos != list->len)
1499      {
1500         Atom *a = (Atom *) mViewChunker.alloc(sizeof(Atom));
1501         a->url = list->url;
1502         a->textStart = list->textStart + breakPos;
1503         a->len = list->len - breakPos;
1504         a->next = list->next;
1505         a->baseLine = list->baseLine;
1506         a->descent = list->descent;
1507         a->style = list->style;
1508         a->isClipped = false;
1509         
1510         list = a;
1511         emit->len = breakPos;
1512         break;
1513      }
1514      list = list->next;
1515      if(totalWidth > width)
1516         break;
1517   }
1518
1519   //if we had to completely clip an atom(s), the last (partially) visible atom should be modified to include a "..."
1520   if (adjustClipAtom && clipAtom)
1521   {
1522      GFont *font = clipAtom->style->font->fontRes;
1523      U32 breakPos;
1524
1525      U32 etcWidth = font->getStrNWidthPrecise("...", 3);
1526
1527      const UTF16 *tmp16 = mTextBuffer.getPtr() + clipAtom->textStart;
1528
1529      breakPos = font->getBreakPos(tmp16, clipAtom->len, clipAtom->width - etcWidth, false);
1530      if (breakPos != 0)
1531      {
1532         clipAtom->isClipped = true;
1533         clipAtom->len = breakPos;
1534      }
1535   }
1536
1537   // terminate the emit list:
1538   *emitPtr = 0;
1539   // now emit it:
1540   // going from mCurX to mCurX + width:
1541   if(mCurJustify == CenterJustify)
1542   {
1543      if ( width > totalWidth )
1544         mCurX += (width - totalWidth) >> 1;
1545   }
1546   else if(mCurJustify == RightJustify)
1547   {
1548      if ( width > totalWidth )
1549         mCurX += width - totalWidth;
1550   }
1551
1552
1553   while(emitList)
1554   {
1555      emitList->xStart = mCurX;
1556      mCurX += emitList->width;
1557      Atom *temp = emitList->next;
1558      *mLineAtomPtr = emitList;
1559      emitList->next = 0;
1560      mLineAtomPtr = &(emitList->next);
1561      emitList = temp;
1562   }
1563   
1564   return list;
1565}
1566
1567//--------------------------------------------------------------------------
1568static bool scanforchar(const char *str, U32 &idx, char c)
1569{
1570   U32 startidx = idx;
1571   while(str[idx] != c && str[idx] && str[idx] != ':' && str[idx] != '>' && str[idx] != '\n')
1572      idx++;
1573   return str[idx] == c && startidx != idx;
1574}
1575
1576//--------------------------------------------------------------------------
1577static bool scanforURL(const char *str, U32 &idx, char c)
1578{
1579   U32 startidx = idx;
1580   while(str[idx] != c && str[idx] && str[idx] != '>' && str[idx] != '\n')
1581      idx++;
1582   return str[idx] == c && startidx != idx;
1583}
1584
1585//--------------------------------------------------------------------------
1586static S32 getHexVal(char c)
1587{
1588   if(c >= '0' && c <= '9')
1589      return c - '0';
1590   else if(c >= 'A' && c <= 'Z')
1591      return c - 'A' + 10;
1592   else if(c >= 'a' && c <= 'z')
1593      return c - 'a' + 10;
1594   return -1;
1595}
1596
1597//--------------------------------------------------------------------------
1598GuiMLTextCtrl::Style *GuiMLTextCtrl::allocStyle(GuiMLTextCtrl::Style *style)
1599{
1600   Style *ret = (Style *) mViewChunker.alloc(sizeof(Style));
1601   ret->used = false;
1602   if(style)
1603   {
1604      ret->font = style->font;
1605      ret->color = style->color;
1606      ret->linkColor = style->linkColor;
1607      ret->linkColorHL = style->linkColorHL;
1608      ret->shadowColor = style->shadowColor;
1609      ret->shadowOffset = style->shadowOffset;
1610      ret->next = style->next;
1611   }
1612   else
1613   {
1614      ret->font = 0;
1615      ret->next = 0;
1616   }
1617   return ret;
1618}
1619
1620//--------------------------------------------------------------------------
1621void GuiMLTextCtrl::reflow()
1622{
1623   AssertFatal(mAwake, "Can't reflow a sleeping control.");
1624   freeLineBuffers();
1625   mDirty = false;
1626   mScanPos = 0;
1627
1628   mLineList = NULL;
1629   mLineInsert = &mLineList;
1630
1631   mCurStyle = allocStyle(NULL);
1632   mCurStyle->font = allocFont((char *) mProfile->mFontType, dStrlen(mProfile->mFontType), mProfile->mFontSize);
1633   if(!mCurStyle->font)
1634      return;
1635   mCurStyle->color = mProfile->mFontColor;
1636   mCurStyle->shadowColor = mProfile->mFontColor;
1637   mCurStyle->shadowOffset.set(0,0);
1638   mCurStyle->linkColor = mProfile->mFontColors[GuiControlProfile::ColorUser0];
1639   mCurStyle->linkColorHL = mProfile->mFontColors[GuiControlProfile::ColorUser1];
1640
1641   U32 width = getWidth();
1642
1643   mCurLMargin = 0;
1644   mCurRMargin = width;
1645   mCurJustify = LeftJustify;
1646   mCurDiv = 0;
1647   mCurY = 0;
1648   mCurX = 0;
1649   mCurClipX = 0;
1650   mLineAtoms = NULL;
1651   mLineAtomPtr = &mLineAtoms;
1652
1653   mSentinel.point.x = width;
1654   mSentinel.point.y = 0;
1655   mSentinel.extent.x = 0;
1656   mSentinel.extent.y = 0x7FFFFF;
1657   mSentinel.nextBlocker = NULL;
1658   mLineStart = 0;
1659   mEmitAtoms = 0;
1660   mMaxY = 0;
1661   mEmitAtomPtr = &mEmitAtoms;
1662
1663   mBlockList = &mSentinel;
1664
1665   Font *nextFont;
1666   LineTag *nextTag;
1667   mTabStops = 0;
1668   mCurTabStop = 0;
1669   mTabStopCount = 0;
1670   mCurURL = 0;
1671   Style *newStyle;
1672
1673   U32 textStart;
1674   U32 len;
1675   U32 idx;
1676   U32 sizidx;
1677
1678   for(;;)
1679   {
1680      UTF16 curChar = mTextBuffer.getChar(mScanPos);
1681
1682      if(!curChar)
1683         break;
1684
1685      if(curChar == '\n')
1686      {
1687         textStart = mScanPos;
1688         len = 1;
1689         mScanPos++;
1690         processEmitAtoms();
1691         emitNewLine(textStart);
1692         mCurDiv = 0;
1693         continue;
1694      }
1695
1696      if(curChar == '\t')
1697      {
1698         textStart = mScanPos;
1699         len = 1;
1700         mScanPos++;
1701         processEmitAtoms();
1702         if(mTabStopCount)
1703         {
1704            if(mCurTabStop < mTabStopCount)
1705            {
1706               if(mCurX < mTabStops[mCurTabStop])
1707                  mCurX = mTabStops[mCurTabStop];
1708            }
1709            mCurTabStop++;
1710         }
1711         continue;
1712      }
1713
1714      if(curChar == '<')
1715      {
1716         // it's probably some kind of tag:
1717
1718         // Get a pointer into the utf8 version of the buffer,
1719         // because we're still scanning text in in utf8 mode.
1720         const UTF8 *str = mTextBuffer.getPtr8();
1721         str = getNthCodepoint(str, mScanPos);
1722
1723         //  And go!
1724
1725         if(!dStrnicmp(str + 1, "br>", 3))
1726         {
1727            mScanPos += 4;
1728            len = 4;
1729            textStart = mScanPos + 4;
1730            processEmitAtoms();
1731            emitNewLine(textStart);
1732            mCurDiv = 0;
1733            continue;
1734         }
1735
1736         if(!dStrnicmp(str + 1, "font:", 5))
1737         {
1738            // scan for the second colon...
1739            // at each level it should drop out to the text case below...
1740            idx = 6;
1741            if(!scanforchar(str, idx, ':'))
1742               goto textemit;
1743
1744            sizidx = idx + 1;
1745            if(!scanforchar(str, sizidx, '>'))
1746               goto textemit;
1747
1748            U32 size = dAtoi(str + idx + 1);
1749            if(!size || size > 64)
1750               goto textemit;
1751            textStart = mScanPos + 6;
1752            len = idx - 6;
1753
1754            mScanPos += sizidx + 1;
1755
1756            nextFont = allocFont(str + 6, len, size);
1757            if(nextFont)
1758            {
1759               if(mCurStyle->used)
1760                  mCurStyle = allocStyle(mCurStyle);
1761               mCurStyle->font = nextFont;
1762            }
1763            continue;
1764         }
1765
1766         if ( !dStrnicmp( str + 1, "tag:", 4 ) )
1767         {
1768            idx = 5;
1769            if ( !scanforchar( str, idx, '>' ) )
1770               goto textemit;
1771            U32 tagId = dAtoi( str + 5 );
1772            nextTag = allocLineTag( tagId );
1773
1774            mScanPos += idx + 1;
1775            continue;
1776         }
1777
1778         if(!dStrnicmp(str +1, "color:", 6))
1779         {
1780            idx = 7;
1781            if(!scanforchar(str, idx, '>'))
1782               goto textemit;
1783            if(idx != 13 && idx != 15)
1784               goto textemit;
1785            ColorI color;
1786
1787            color.red = getHexVal(str[7]) * 16 + getHexVal(str[8]);
1788            color.green = getHexVal(str[9]) * 16 + getHexVal(str[10]);
1789            color.blue = getHexVal(str[11]) * 16 + getHexVal(str[12]);
1790            if(idx == 15)
1791               color.alpha = getHexVal(str[13]) * 16 + getHexVal(str[14]);
1792            else
1793               color.alpha = 255;
1794            mScanPos += idx + 1;
1795
1796            if(mCurStyle->used)
1797               mCurStyle = allocStyle(mCurStyle);
1798            mCurStyle->color = color;
1799
1800            continue;
1801         }
1802
1803         if(!dStrnicmp(str +1, "shadowcolor:", 12))
1804         {
1805            idx = 13;
1806            if(!scanforchar(str, idx, '>'))
1807               goto textemit;
1808            if(idx != 19 && idx != 21)
1809               goto textemit;
1810            ColorI color;
1811
1812            color.red   = getHexVal(str[13]) * 16 + getHexVal(str[14]);
1813            color.green = getHexVal(str[15]) * 16 + getHexVal(str[16]);
1814            color.blue  = getHexVal(str[17]) * 16 + getHexVal(str[18]);
1815            if(idx == 21)
1816               color.alpha = getHexVal(str[19]) * 16 + getHexVal(str[20]);
1817            else
1818               color.alpha = 255;
1819            mScanPos += idx + 1;
1820
1821            if(mCurStyle->used)
1822               mCurStyle = allocStyle(mCurStyle);
1823            mCurStyle->shadowColor = color;
1824
1825            continue;
1826         }
1827
1828         if(!dStrnicmp(str +1, "linkcolor:", 10))
1829         {
1830            idx = 11;
1831            if(!scanforchar(str, idx, '>'))
1832               goto textemit;
1833            if(idx != 17 && idx != 19)
1834               goto textemit;
1835            ColorI color;
1836
1837            color.red   = getHexVal(str[11]) * 16 + getHexVal(str[12]);
1838            color.green = getHexVal(str[13]) * 16 + getHexVal(str[14]);
1839            color.blue  = getHexVal(str[15]) * 16 + getHexVal(str[16]);
1840            if(idx == 19)
1841               color.alpha = getHexVal(str[17]) * 16 + getHexVal(str[18]);
1842            else
1843               color.alpha = 255;
1844            mScanPos += idx + 1;
1845
1846            if(mCurStyle->used)
1847               mCurStyle = allocStyle(mCurStyle);
1848            mCurStyle->linkColor = color;
1849
1850            continue;
1851         }
1852
1853         if(!dStrnicmp(str +1, "linkcolorhl:", 12))
1854         {
1855            idx = 13;
1856            if(!scanforchar(str, idx, '>'))
1857               goto textemit;
1858            if(idx != 19 && idx != 21)
1859               goto textemit;
1860            ColorI color;
1861
1862            color.red = getHexVal(str[13]) * 16 + getHexVal(str[14]);
1863            color.green = getHexVal(str[15]) * 16 + getHexVal(str[16]);
1864            color.blue = getHexVal(str[17]) * 16 + getHexVal(str[18]);
1865            if(idx == 21)
1866               color.alpha = getHexVal(str[19]) * 16 + getHexVal(str[20]);
1867            else
1868               color.alpha = 255;
1869            mScanPos += idx + 1;
1870
1871            if(mCurStyle->used)
1872               mCurStyle = allocStyle(mCurStyle);
1873            mCurStyle->linkColorHL = color;
1874
1875            continue;
1876         }
1877         if(!dStrnicmp(str +1, "shadow:", 7))
1878         {
1879            idx = 8;
1880            if(!scanforchar(str, idx, ':'))
1881               goto textemit;
1882            U32 yidx = idx + 1;
1883            if(!scanforchar(str, yidx, '>'))
1884               goto textemit;
1885            mScanPos += yidx + 1;
1886            Point2I offset;
1887            offset.x = dAtoi(str + 8);
1888            offset.y = dAtoi(str + idx + 1);
1889            if(mCurStyle->used)
1890               mCurStyle = allocStyle(mCurStyle);
1891            mCurStyle->shadowOffset = offset;
1892            continue;
1893         }
1894         if(!dStrnicmp(str +1, "bitmap", 6))
1895         {
1896            S32 start = 8;
1897            bool bitBrk = false;
1898            if(str[7] == 'k' && str[8] == ':')
1899            {
1900               bitBrk = true;
1901               start = 9;
1902            }
1903            else if(str[7] != ':')
1904               goto textemit;
1905
1906            idx = start;
1907            if(!scanforchar(str, idx, '>'))
1908               goto textemit;
1909            textStart = mScanPos + start;
1910            len = idx - start;
1911
1912            mScanPos += idx + 1;
1913
1914            processEmitAtoms();
1915            Bitmap *bmp;
1916            bmp = allocBitmap(str + 8, len);
1917            if(bmp)
1918               emitBitmapToken(bmp, textStart, bitBrk);
1919            continue;
1920         }
1921
1922         if(!dStrnicmp(str +1, "spush>", 6))
1923         {
1924            mScanPos += 7;
1925            newStyle = allocStyle(mCurStyle); // copy out all the attributes...
1926            newStyle->next = mCurStyle;
1927            mCurStyle = newStyle;
1928            continue;
1929         }
1930
1931         if(!dStrnicmp(str +1, "spop>", 5))
1932         {
1933            mScanPos += 6;
1934            if(mCurStyle->next)
1935               mCurStyle = mCurStyle->next;
1936            continue;
1937         }
1938
1939         if(!dStrnicmp(str +1, "sbreak>", 7))
1940         {
1941            mScanPos += 8;
1942            processEmitAtoms();
1943            while(mBlockList != &mSentinel)
1944               emitNewLine(mScanPos);
1945            continue;
1946         }
1947
1948         if(!dStrnicmp(str +1, "just:left>", 10))
1949         {
1950            processEmitAtoms();
1951            mCurJustify = LeftJustify;
1952            mScanPos += 11;
1953            continue;
1954         }
1955
1956         if(!dStrnicmp(str +1, "just:right>", 11))
1957         {
1958            processEmitAtoms();
1959            mCurJustify = RightJustify;
1960            mScanPos += 12;
1961            continue;
1962         }
1963
1964         if(!dStrnicmp(str +1, "just:center>", 12))
1965         {
1966            processEmitAtoms();
1967            mCurJustify = CenterJustify;
1968            mScanPos += 13;
1969            continue;
1970         }
1971
1972         if(!dStrnicmp(str +1, "a:", 2))
1973         {
1974            idx = 3;
1975            if(!scanforURL(str, idx, '>'))
1976               goto textemit;
1977
1978            mCurURL = (URL *) mViewChunker.alloc(sizeof(URL));
1979            mCurURL->mouseDown = false;
1980            mCurURL->textStart = mScanPos + 3;
1981            mCurURL->len = idx - 3;
1982            mCurURL->noUnderline = false;
1983
1984            //if the URL is a "gamelink", don't underline...
1985            if (!dStrnicmp(str + 3, "gamelink", 8))
1986               mCurURL->noUnderline = true;
1987
1988            mScanPos += idx + 1;
1989            continue;
1990         }
1991
1992         if(!dStrnicmp(str+1, "/a>", 3))
1993         {
1994            mCurURL = NULL;
1995            mScanPos += 4;
1996            continue;
1997         }
1998
1999         U32 margin;
2000
2001         if(!dStrnicmp(str + 1, "lmargin%:", 9))
2002         {
2003            idx = 10;
2004            if(!scanforchar(str, idx, '>'))
2005               goto textemit;
2006            margin = (getWidth() * dAtoi(str + 10)) / 100;
2007            mScanPos += idx + 1;
2008            goto setleftmargin;
2009         }
2010
2011         if(!dStrnicmp(str + 1, "lmargin:", 8))
2012         {
2013            idx = 9;
2014            if(!scanforchar(str, idx, '>'))
2015               goto textemit;
2016            margin = dAtoi(str + 9);
2017            mScanPos += idx + 1;
2018setleftmargin:
2019            processEmitAtoms();
2020            U32 oldLMargin;
2021            oldLMargin = mCurLMargin;
2022            mCurLMargin = margin;
2023            if(mCurLMargin >= width)
2024               mCurLMargin = width - 1;
2025            if(mCurX == oldLMargin)
2026               mCurX = mCurLMargin;
2027            if(mCurX < mCurLMargin)
2028               mCurX = mCurLMargin;
2029            continue;
2030         }
2031
2032         if(!dStrnicmp(str + 1, "rmargin%:", 9))
2033         {
2034            idx = 10;
2035            if(!scanforchar(str, idx, '>'))
2036               goto textemit;
2037            margin = (getWidth() * dAtoi(str + 10)) / 100;
2038            mScanPos += idx + 1;
2039            goto setrightmargin;
2040         }
2041
2042         if(!dStrnicmp(str + 1, "rmargin:", 8))
2043         {
2044            idx = 9;
2045            if(!scanforchar(str, idx, '>'))
2046               goto textemit;
2047            margin = dAtoi(str + 9);
2048            mScanPos += idx + 1;
2049setrightmargin:
2050            processEmitAtoms();
2051            mCurRMargin = margin;
2052            if(mCurLMargin >= width)
2053               mCurLMargin = width;
2054            if (mCurClipX > mCurRMargin)
2055               mCurClipX = mCurRMargin;
2056            continue;
2057         }
2058
2059         if(!dStrnicmp(str + 1, "clip:", 5))
2060         {
2061            U32 clipWidth = 0;
2062            idx = 6;
2063            if(!scanforchar(str, idx, '>'))
2064               goto textemit;
2065            clipWidth = dAtoi(str + 6);
2066            mScanPos += idx + 1;
2067            processEmitAtoms();
2068            if (clipWidth > 0)
2069               mCurClipX = mCurX + clipWidth;
2070            else
2071               mCurClipX = 0;
2072            if(mCurClipX > mCurRMargin)
2073               mCurClipX = mCurRMargin;
2074            continue;
2075         }
2076
2077         if(!dStrnicmp(str + 1, "/clip>", 6))
2078         {
2079            processEmitAtoms();
2080            mCurClipX = 0;
2081            mScanPos += 7;
2082            continue;
2083         }
2084
2085         if(!dStrnicmp(str + 1, "div:", 4))
2086         {
2087            idx = 5;
2088            if(!scanforchar(str, idx, '>'))
2089               goto textemit;
2090            mScanPos += idx + 1;
2091            mCurDiv = dAtoi(str + 5);
2092            continue;
2093         }
2094
2095         if(!dStrnicmp(str + 1, "tab:", 4))
2096         {
2097            idx = 5;
2098            if(!scanforchar(str, idx, '>'))
2099               goto textemit;
2100            // scan for tab stops...
2101            mTabStopCount = 1;
2102            idx = 5;
2103            while(scanforchar(str, idx, ','))
2104            {
2105               idx++;
2106               mTabStopCount++;
2107            }
2108            idx = 5;
2109            mTabStops = (U32 *) mViewChunker.alloc(sizeof(U32) * mTabStopCount);
2110            mTabStops[0] = dAtoi(str + idx);
2111            U32 i = 1;
2112
2113            while(scanforchar(str, idx, ','))
2114            {
2115               idx++;
2116               mTabStops[i] = dAtoi(str + idx);
2117               i++;
2118            }
2119            mScanPos += idx + 1;
2120            continue;
2121         }
2122      }
2123
2124      // default case:
2125textemit:
2126      textStart = mScanPos;
2127      idx = 1;
2128      while(mTextBuffer.getChar(mScanPos+idx) != '\t' && mTextBuffer.getChar(mScanPos+idx) != '<' && mTextBuffer.getChar(mScanPos+idx) != '\n' && mTextBuffer.getChar(mScanPos+idx))
2129         idx++;
2130      len = idx;
2131      mScanPos += idx;
2132      emitTextToken(textStart, len);
2133   }
2134   processEmitAtoms();
2135   emitNewLine(mScanPos);
2136   setHeight( mMaxY );
2137   onResize_callback( getWidth(), mMaxY );
2138   
2139   //make sure the cursor is still visible - this handles if we're a child of a scroll ctrl...
2140   ensureCursorOnScreen();
2141}
2142
2143//-----------------------------------------------------------------------------
2144char* GuiMLTextCtrl::stripControlChars(const char *inString)
2145{
2146   if (! bool(inString))
2147      return NULL;
2148   U32 maxBufLength = 64;
2149   char *strippedBuffer = Con::getReturnBuffer(maxBufLength);
2150   char *stripBufPtr = &strippedBuffer[0];
2151   const char *bufPtr = (char *) inString;
2152   U32 idx, sizidx;
2153
2154   for(;;)
2155   {
2156      //if we've reached the end of the string, or run out of room in the stripped Buffer...
2157      if(*bufPtr == '\0' || (U32(stripBufPtr - strippedBuffer) >= maxBufLength - 1))
2158         break;
2159
2160      if (*bufPtr == '\n')
2161      {
2162         U32 walked;
2163         oneUTF8toUTF32(bufPtr,&walked);
2164         bufPtr += walked; 
2165         continue;
2166      }
2167      if(*bufPtr == '\t')
2168      {
2169         U32 walked;
2170         oneUTF8toUTF32(bufPtr,&walked);
2171         bufPtr += walked;
2172         continue;
2173      }
2174      if(*bufPtr < 0x20 && *bufPtr >= 0)
2175      {
2176         U32 walked;
2177         oneUTF8toUTF32(bufPtr,&walked);
2178         bufPtr += walked;
2179         continue;
2180      }
2181
2182      if(*bufPtr == '<')
2183      {
2184         // it's probably some kind of tag:
2185         if(!dStrnicmp(bufPtr + 1, "font:", 5))
2186         {
2187            // scan for the second colon...
2188            // at each level it should drop out to the text case below...
2189            idx = 6;
2190            if(!scanforchar((char*)bufPtr, idx, ':'))
2191               goto textemit;
2192
2193            sizidx = idx + 1;
2194            if(!scanforchar((char*)bufPtr, sizidx, '>'))
2195               goto textemit;
2196
2197            bufPtr += sizidx + 1;
2198            continue;
2199         }
2200
2201         if (!dStrnicmp(bufPtr + 1, "tag:", 4 ))
2202         {
2203            idx = 5;
2204            if ( !scanforchar((char*)bufPtr, idx, '>' ))
2205               goto textemit;
2206
2207            bufPtr += idx + 1;
2208            continue;
2209         }
2210
2211         if(!dStrnicmp(bufPtr + 1, "color:", 6))
2212         {
2213            idx = 7;
2214            if(!scanforchar((char*)bufPtr, idx, '>'))
2215               goto textemit;
2216            if(idx != 13)
2217               goto textemit;
2218
2219            bufPtr += 14;
2220            continue;
2221         }
2222
2223         if(!dStrnicmp(bufPtr +1, "bitmap:", 7))
2224         {
2225            idx = 8;
2226            if(!scanforchar((char*)bufPtr, idx, '>'))
2227               goto textemit;
2228
2229            bufPtr += idx + 1;
2230            continue;
2231         }
2232
2233         if(!dStrnicmp(bufPtr +1, "spush>", 6))
2234         {
2235            bufPtr += 7;
2236            continue;
2237         }
2238
2239         if(!dStrnicmp(bufPtr +1, "spop>", 5))
2240         {
2241            bufPtr += 6;
2242            continue;
2243         }
2244
2245         if(!dStrnicmp(bufPtr +1, "sbreak>", 7))
2246         {
2247            bufPtr += 8;
2248            continue;
2249         }
2250
2251         if(!dStrnicmp(bufPtr +1, "just:left>", 10))
2252         {
2253            bufPtr += 11;
2254            continue;
2255         }
2256
2257         if(!dStrnicmp(bufPtr +1, "just:right>", 11))
2258         {
2259            bufPtr += 12;
2260            continue;
2261         }
2262
2263         if(!dStrnicmp(bufPtr +1, "just:center>", 12))
2264         {
2265            bufPtr += 13;
2266            continue;
2267         }
2268
2269         if(!dStrnicmp(bufPtr +1, "a:", 2))
2270         {
2271            idx = 3;
2272            if(!scanforchar((char*)bufPtr, idx, '>'))
2273               goto textemit;
2274
2275            bufPtr += idx + 1;
2276            continue;
2277         }
2278
2279         if(!dStrnicmp(bufPtr+1, "/a>", 3))
2280         {
2281            bufPtr += 4;
2282            continue;
2283         }
2284
2285         if(!dStrnicmp(bufPtr + 1, "lmargin%:", 9))
2286         {
2287            idx = 10;
2288            if(!scanforchar((char*)bufPtr, idx, '>'))
2289               goto textemit;
2290            bufPtr += idx + 1;
2291            goto setleftmargin;
2292         }
2293
2294         if(!dStrnicmp(bufPtr + 1, "lmargin:", 8))
2295         {
2296            idx = 9;
2297            if(!scanforchar((char*)bufPtr, idx, '>'))
2298               goto textemit;
2299            bufPtr += idx + 1;
2300setleftmargin:
2301            continue;
2302         }
2303
2304         if(!dStrnicmp(bufPtr + 1, "rmargin%:", 9))
2305         {
2306            idx = 10;
2307            if(!scanforchar((char*)bufPtr, idx, '>'))
2308               goto textemit;
2309            bufPtr += idx + 1;
2310            goto setrightmargin;
2311         }
2312
2313         if(!dStrnicmp(bufPtr + 1, "rmargin:", 8))
2314         {
2315            idx = 9;
2316            if(!scanforchar((char*)bufPtr, idx, '>'))
2317               goto textemit;
2318            bufPtr += idx + 1;
2319setrightmargin:
2320            continue;
2321         }
2322
2323         if(!dStrnicmp(bufPtr + 1, "clip:", 5))
2324         {
2325            idx = 6;
2326            if(!scanforchar((char*)bufPtr, idx, '>'))
2327               goto textemit;
2328            bufPtr += idx + 1;
2329            continue;
2330         }
2331
2332         if(!dStrnicmp(bufPtr + 1, "/clip>", 6))
2333         {
2334            bufPtr += 7;
2335            continue;
2336         }
2337
2338         if(!dStrnicmp(bufPtr + 1, "div:", 4))
2339         {
2340            idx = 5;
2341            if(!scanforchar((char*)bufPtr, idx, '>'))
2342               goto textemit;
2343            bufPtr += idx + 1;
2344            continue;
2345         }
2346
2347         if(!dStrnicmp(bufPtr + 1, "tab:", 4))
2348         {
2349            idx = 5;
2350            if(!scanforchar((char*)bufPtr, idx, '>'))
2351               goto textemit;
2352            bufPtr += idx + 1;
2353            continue;
2354         }
2355      }
2356
2357      // default case:
2358textemit:
2359      *stripBufPtr++ = *bufPtr++;
2360      while(*bufPtr != '\t' && *bufPtr != '<' && *bufPtr != '\n' && (*bufPtr >= 0x20 || *bufPtr < 0))
2361         *stripBufPtr++ = *bufPtr++;
2362   }
2363
2364   //we're finished - terminate the string
2365   *stripBufPtr = '\0';
2366   return strippedBuffer;
2367}
2368
2369//--------------------------------------------------------------------------
2370
2371bool GuiMLTextCtrl::resize( const Point2I& newPosition, const Point2I& newExtent )
2372{
2373   if( Parent::resize( newPosition, newExtent ) )
2374   {
2375      mDirty = true;
2376      return true;
2377   }
2378   
2379   return false;
2380}
2381