guiMLTextCtrl.cpp
Engine/source/gui/controls/guiMLTextCtrl.cpp
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)
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>" )
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