Torque3D Documentation / _generateds / guiMessageVectorCtrl.cpp

guiMessageVectorCtrl.cpp

Engine/source/gui/game/guiMessageVectorCtrl.cpp

More...

Classes:

Public Functions

ConsoleDocClass(GuiMessageVectorCtrl , "@brief A chat HUD <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that displays messages from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MessageVector.\n\n</a>" "This renders messages from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classmessagevector/">MessageVector</a>; the important thing " "here is that the <a href="/coding/class/classmessagevector/">MessageVector</a> holds all the messages we care " " about, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> we can destroy and create these GUI controls as " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">needed.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Declare ChatHud, which is what will display the actual chat from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MessageVector\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimessagevectorctrl/">GuiMessageVectorCtrl</a>(ChatHud) {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " profile=\"ChatHudMessageProfile\";\n" "   horizSizing = \"width\";\n" "   vertSizing = \"height\";\n" "   position = \"1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   extent = \"252 16\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minExtent = \"8 8\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   visible = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   helpTag = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lineSpacing = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lineContinuedIndex = \"10\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   matchColor = \"0 0 255 255\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxColorIndex = \"5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// All messages are stored in this HudMessageVector, the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">actual\n</a>" "//MainChatHud only displays the contents of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classmessagevector/">MessageVector</a>(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Attach the <a href="/coding/class/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the chat <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "chatHud.attach(HudMessageVector);\<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/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> more details on how this is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">used\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiUtil\n</a>" )
DefineEngineMethod(GuiMessageVectorCtrl , attach , bool , (MessageVector *item) , "@brief Push <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> line onto the back of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "@param item The GUI element being pushed into 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>" "// All messages are stored in this HudMessageVector, the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">actual\n</a>" "//MainChatHud only displays the contents of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classmessagevector/">MessageVector</a>(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Attach the <a href="/coding/class/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the chat <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "chatHud.attach(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @return <a href="/coding/file/document_8h/#document_8h_1a071cf97155ba72ac9a1fc4ad7e63d481">Value</a>" )
DefineEngineMethod(GuiMessageVectorCtrl , detach , void , () , "@brief Stop listing messages from the <a href="/coding/class/classmessagevector/">MessageVector</a> previously attached to, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n\n</a>" "Detailed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">description\n\n</a>" " @param param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Description\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Deatch the <a href="/coding/class/classmessagevector/">MessageVector</a> from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">HudMessageVector\n</a>" "//HudMessageVector will no longer <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text\n</a>" "chatHud.detach();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(GuiMessageVectorCtrl , "@brief A chat HUD <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that displays messages from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MessageVector.\n\n</a>" "This renders messages from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classmessagevector/">MessageVector</a>; the important thing " "here is that the <a href="/coding/class/classmessagevector/">MessageVector</a> holds all the messages we care " " about, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a0e48c1f64b558d03d870367324920354">while</a> we can destroy and create these GUI controls as " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">needed.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Declare ChatHud, which is what will display the actual chat from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">MessageVector\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguimessagevectorctrl/">GuiMessageVectorCtrl</a>(ChatHud) {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " profile=\"ChatHudMessageProfile\";\n" "   horizSizing = \"width\";\n" "   vertSizing = \"height\";\n" "   position = \"1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   extent = \"252 16\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minExtent = \"8 8\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   visible = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   helpTag = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lineSpacing = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lineContinuedIndex = \"10\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   matchColor = \"0 0 255 255\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxColorIndex = \"5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// All messages are stored in this HudMessageVector, the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">actual\n</a>" "//MainChatHud only displays the contents of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classmessagevector/">MessageVector</a>(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Attach the <a href="/coding/class/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the chat <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "chatHud.attach(HudMessageVector);\<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/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> more details on how this is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">used\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiUtil\n</a>" )

DefineEngineMethod(GuiMessageVectorCtrl , attach , bool , (MessageVector *item) , "@brief Push <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> line onto the back of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n\n</a>" "@param item The GUI element being pushed into 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>" "// All messages are stored in this HudMessageVector, the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">actual\n</a>" "//MainChatHud only displays the contents of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classmessagevector/">MessageVector</a>(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Attach the <a href="/coding/class/classmessagevector/">MessageVector</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the chat <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n</a>" "chatHud.attach(HudMessageVector);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @return <a href="/coding/file/document_8h/#document_8h_1a071cf97155ba72ac9a1fc4ad7e63d481">Value</a>" )

DefineEngineMethod(GuiMessageVectorCtrl , detach , void , () , "@brief Stop listing messages from the <a href="/coding/class/classmessagevector/">MessageVector</a> previously attached to, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n\n</a>" "Detailed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">description\n\n</a>" " @param param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Description\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Deatch the <a href="/coding/class/classmessagevector/">MessageVector</a> from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">HudMessageVector\n</a>" "//HudMessageVector will no longer <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text\n</a>" "chatHud.detach();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" )

IMPLEMENT_CONOBJECT(GuiMessageVectorCtrl )

sMVCtrlCallback(void * spectatorKey, const MessageVector::MessageCode code, const U32 argument)

  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 "platform/platform.h"
 25#include "gui/game/guiMessageVectorCtrl.h"
 26
 27#include "gui/utility/messageVector.h"
 28#include "console/consoleTypes.h"
 29#include "gui/containers/guiScrollCtrl.h"
 30#include "gfx/gfxDevice.h"
 31#include "gfx/gfxDrawUtil.h"
 32#include "console/engineAPI.h"
 33
 34IMPLEMENT_CONOBJECT(GuiMessageVectorCtrl);
 35
 36ConsoleDocClass( GuiMessageVectorCtrl,
 37   "@brief A chat HUD control that displays messages from a MessageVector.\n\n"
 38
 39   "This renders messages from a MessageVector; the important thing "
 40   "here is that the MessageVector holds all the messages we care "
 41   "about, while we can destroy and create these GUI controls as "
 42   "needed.\n\n"
 43
 44   "@tsexample\n"
 45   "// Declare ChatHud, which is what will display the actual chat from a MessageVector\n"
 46   "new GuiMessageVectorCtrl(ChatHud) {\n"
 47    "   profile = \"ChatHudMessageProfile\";\n"
 48    "   horizSizing = \"width\";\n"
 49    "   vertSizing = \"height\";\n"
 50    "   position = \"1 1\";\n"
 51    "   extent = \"252 16\";\n"
 52    "   minExtent = \"8 8\";\n"
 53    "   visible = \"1\";\n"
 54    "   helpTag = \"0\";\n"
 55    "   lineSpacing = \"0\";\n"
 56    "   lineContinuedIndex = \"10\";\n"
 57    "   matchColor = \"0 0 255 255\";\n"
 58    "   maxColorIndex = \"5\";\n"
 59    "};\n\n"
 60   "// All messages are stored in this HudMessageVector, the actual\n"
 61   "// MainChatHud only displays the contents of this vector.\n"
 62   "new MessageVector(HudMessageVector);\n\n"
 63   "// Attach the MessageVector to the chat control\n"
 64   "chatHud.attach(HudMessageVector);\n"
 65   "@endtsexample\n\n"
 66
 67   "@see MessageVector for more details on how this is used\n"
 68
 69   "@ingroup GuiUtil\n");
 70
 71
 72//-------------------------------------- Console functions
 73DefineEngineMethod( GuiMessageVectorCtrl, attach, bool, ( MessageVector* item),,
 74      "@brief Push a line onto the back of the list.\n\n"
 75
 76      "@param item The GUI element being pushed into the control\n\n"
 77
 78      "@tsexample\n"
 79      "// All messages are stored in this HudMessageVector, the actual\n"
 80      "// MainChatHud only displays the contents of this vector.\n"
 81      "new MessageVector(HudMessageVector);\n\n"
 82      "// Attach the MessageVector to the chat control\n"
 83      "chatHud.attach(HudMessageVector);\n"
 84      "@endtsexample\n\n"
 85
 86      "@return Value")
 87{
 88   if (item == NULL)
 89   {
 90      Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", item);
 91      return false;
 92   }
 93   
 94   return object->attach(item);
 95}
 96
 97//ConsoleMethod(GuiMessageVectorCtrl, attach, bool, 3, 3, "(MessageVector item)"
 98//              "Make this gui control display messages from the specified MessageVector")
 99//{
100//   MessageVector* pMV = NULL;
101//   Sim::findObject(argv[2], pMV);
102//   if (pMV == NULL) {
103//      Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", argv[2]);
104//      return false;
105//   }
106//
107//   return object->attach(pMV);
108//}
109
110DefineEngineMethod( GuiMessageVectorCtrl, detach, void, (),,
111               "@brief Stop listing messages from the MessageVector previously attached to, if any.\n\n"
112
113               "Detailed description\n\n"
114
115               "@param param Description\n\n"
116
117               "@tsexample\n"
118               "// Deatch the MessageVector from HudMessageVector\n"
119               "// HudMessageVector will no longer render the text\n"
120               "chatHud.detach();\n"
121               "@endtsexample\n\n")
122{
123   if (object->isAttached() == false)
124   {
125      Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach");
126      return;
127   }
128
129   object->detach();
130}
131
132//ConsoleMethod(GuiMessageVectorCtrl, detach, void, 2, 2, "()"
133//              "Stop listing messages from the MessageVector previously attached to, if any.")
134//{
135//   if (object->isAttached() == false) {
136//      Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach");
137//      return;
138//   }
139//
140//   object->detach();
141//}
142
143struct TempLineBreak
144{
145   S32 start;
146   S32 end;
147};
148
149//--------------------------------------------------------------------------
150// Callback for messageVector
151void sMVCtrlCallback(void *                           spectatorKey,
152                     const MessageVector::MessageCode code,
153                     const U32                        argument)
154{
155   GuiMessageVectorCtrl* pMVC = reinterpret_cast<GuiMessageVectorCtrl*>(spectatorKey);
156   pMVC->callbackRouter(code, argument);
157}
158
159
160//--------------------------------------------------------------------------
161GuiMessageVectorCtrl::GuiMessageVectorCtrl()
162{
163   VECTOR_SET_ASSOCIATION(mLineWrappings);
164   VECTOR_SET_ASSOCIATION(mSpecialMarkers);
165   VECTOR_SET_ASSOCIATION(mLineElements);
166
167   mMessageVector = NULL;
168   mMinSensibleWidth = 1;
169   mLineSpacingPixels = 0;
170   mLineContinuationIndent = 10;
171
172   mMouseDown      = false;
173   mMouseSpecialLine = -1;
174   mMouseSpecialRef  = -1;
175
176   for (U32 i = 0; i < 16; i++)
177      mAllowedMatches[i] = "";
178   mSpecialColor.set(0, 0, 255);
179
180   mMaxColorIndex = 9;
181}
182
183
184//--------------------------------------------------------------------------
185GuiMessageVectorCtrl::~GuiMessageVectorCtrl()
186{
187   AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
188   AssertFatal(mSpecialMarkers.size() == 0, "Error, special markers not properly cleared!");
189   AssertFatal(mLineElements.size() == 0, "Error, line elements not properly cleared!");
190}
191
192
193//--------------------------------------------------------------------------
194void GuiMessageVectorCtrl::initPersistFields()
195{
196   addField("lineSpacing",        TypeS32,    Offset(mLineSpacingPixels,      GuiMessageVectorCtrl));
197   addField("lineContinuedIndex", TypeS32,    Offset(mLineContinuationIndent, GuiMessageVectorCtrl));
198   addField("allowedMatches",     TypeString, Offset(mAllowedMatches,         GuiMessageVectorCtrl), 16);
199   addField("matchColor",         TypeColorI, Offset(mSpecialColor,           GuiMessageVectorCtrl));
200   addField("maxColorIndex",      TypeS32,    Offset(mMaxColorIndex,          GuiMessageVectorCtrl));
201   Parent::initPersistFields();
202}
203
204
205bool GuiMessageVectorCtrl::onAdd()
206{
207   return Parent::onAdd();
208}
209
210
211void GuiMessageVectorCtrl::onRemove()
212{
213   Parent::onRemove();
214}
215
216
217//--------------------------------------------------------------------------
218bool GuiMessageVectorCtrl::isAttached() const
219{
220   return (mMessageVector != NULL);
221}
222
223
224//--------------------------------------------------------------------------
225bool GuiMessageVectorCtrl::attach(MessageVector* newAttachment)
226{
227   AssertFatal(newAttachment, "No attachment!");
228   if (newAttachment == NULL || !isAwake())
229      return false;
230
231   if (isAttached()) {
232      Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::attach: overriding attachment");
233      detach();
234   }
235   AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
236
237   mMessageVector = newAttachment;
238   mMessageVector->registerSpectator(sMVCtrlCallback, this);
239
240   return true;
241}
242
243//--------------------------------------------------------------------------
244void GuiMessageVectorCtrl::detach()
245{
246   if (isAttached() == false) {
247      Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::detach: not attached!");
248      return;
249   }
250
251   mMessageVector->unregisterSpectator(this);
252   mMessageVector = NULL;
253   AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
254}
255
256
257//--------------------------------------------------------------------------
258void GuiMessageVectorCtrl::lineInserted(const U32 arg)
259{
260   AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
261
262   GuiScrollCtrl* pScroll = dynamic_cast<GuiScrollCtrl*>(getParent());
263   bool fullyScrolled = pScroll->isScrolledToBottom();
264
265   mSpecialMarkers.insert(arg);
266   createSpecialMarkers(mSpecialMarkers[arg], mMessageVector->getLine(arg).message);
267
268   mLineWrappings.insert(arg);
269   createLineWrapping(mLineWrappings[arg], mMessageVector->getLine(arg).message);
270
271   mLineElements.insert(arg);
272   createLineElement(mLineElements[arg], mLineWrappings[arg], mSpecialMarkers[arg]);
273
274   U32 numLines = 0;
275   for (U32 i = 0; i < mLineWrappings.size(); i++) {
276      // We need to rebuild the physicalLineStart markers at the same time as
277      //  we find out how many of them are left...
278      mLineElements[i].physicalLineStart = numLines;
279
280      numLines += mLineWrappings[i].numLines;
281   }
282
283   Point2I newExtent = getExtent();
284   newExtent.y = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
285   setExtent(newExtent);
286   if(fullyScrolled)
287      pScroll->scrollTo(0, 0x7FFFFFFF);
288}
289
290
291void GuiMessageVectorCtrl::lineDeleted(const U32 arg)
292{
293   AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
294   AssertFatal(arg < mLineWrappings.size(), "Error, out of bounds line deleted!");
295
296   // It's a somewhat involved process to delete the lineelements...
297   LineElement& rElement = mLineElements[arg];
298
299   TextElement* walk = rElement.headLineElements;
300   while (walk != NULL) {
301      TextElement* lineWalk = walk->nextInLine;
302      while (lineWalk != NULL) {
303         TextElement* temp = lineWalk;
304         lineWalk = lineWalk->nextPhysicalLine;
305         delete temp;
306      }
307
308      TextElement* temp = walk;
309      walk = walk->nextPhysicalLine;
310      delete temp;
311   }
312   rElement.headLineElements = NULL;
313   mLineElements.erase(arg);
314
315   delete [] mLineWrappings[arg].startEndPairs;
316   mLineWrappings.erase(arg);
317
318   delete [] mSpecialMarkers[arg].specials;
319   mSpecialMarkers.erase(arg);
320
321   U32 numLines = 0;
322   for (U32 i = 0; i < mLineWrappings.size(); i++) {
323      // We need to rebuild the physicalLineStart markers at the same time as
324      //  we find out how many of them are left...
325      mLineElements[i].physicalLineStart = numLines;
326
327      numLines += mLineWrappings[i].numLines;
328   }
329
330   U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
331   resize(getPosition(), Point2I(getWidth(), newHeight));
332}
333
334
335void GuiMessageVectorCtrl::vectorDeleted()
336{
337   AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
338   AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared out!");
339
340   mMessageVector = NULL;
341   U32 newHeight = mProfile->mFont->getHeight() + mLineSpacingPixels;
342   resize(getPosition(), Point2I(getWidth(), newHeight));
343}
344
345
346void GuiMessageVectorCtrl::callbackRouter(const MessageVector::MessageCode code,
347                                          const U32                        arg)
348{
349   switch (code) {
350      case MessageVector::LineInserted:
351         lineInserted(arg);
352         break;
353      case MessageVector::LineDeleted:
354         lineDeleted(arg);
355         break;
356      case MessageVector::VectorDeletion:
357         vectorDeleted();
358         break;
359   }
360}
361
362
363//--------------------------------------------------------------------------
364void GuiMessageVectorCtrl::createSpecialMarkers(SpecialMarkers& rSpecial, const char* string)
365{
366   // The first thing we need to do is create a version of the string with no uppercase
367   //  chars for matching...
368
369   String pLCCopyStr = String::ToLower( string );
370   const char* pLCCopy = pLCCopyStr.c_str();
371
372   Vector<TempLineBreak> tempSpecials(__FILE__, __LINE__);
373   Vector<S32>           tempTypes(__FILE__, __LINE__);
374
375   const char* pCurr = pLCCopy;
376   while (pCurr[0] != '\0') {
377      const char* pMinMatch = &pLCCopy[dStrlen(string)];
378      U32 minMatchType = 0xFFFFFFFF;
379      AssertFatal(pMinMatch[0] == '\0', "Error, bad positioning of sentry...");
380
381      // Find the earliest match
382      for (U32 i = 0; i < 16; i++) {
383         if (mAllowedMatches[i][0] == '\0')
384            continue;
385
386         const char* pMatch = dStrstr(pCurr, mAllowedMatches[i]);
387         if (pMatch != NULL && pMatch < pMinMatch) {
388            pMinMatch = pMatch;
389            minMatchType = i;
390         }
391      }
392
393      if (pMinMatch[0] != '\0') {
394         AssertFatal(minMatchType != 0xFFFFFFFF, "Hm, that's bad");
395         // Found a match => now find the end
396         U32 start = pMinMatch - pLCCopy;
397         U32 j;
398         for (j = 1; pLCCopy[start + j] != '\0'; j++) {
399            if (pLCCopy[start + j] == '\n' ||
400                pLCCopy[start + j] == ' '  ||
401                pLCCopy[start + j] == '\t')
402               break;
403         }
404         AssertFatal(j > 0, "Error, j must be > 0 at this point!");
405         U32 end = start + j - 1;
406
407         tempSpecials.increment();
408         tempSpecials.last().start = start;
409         tempSpecials.last().end   = end;
410         tempTypes.push_back(minMatchType);
411
412         pCurr  = &pLCCopy[end + 1];
413      } else {
414         // No match.  This will cause the while loop to terminate...
415         pCurr = pMinMatch;
416      }
417   }
418
419   if ((rSpecial.numSpecials = tempSpecials.size()) != 0) {
420      rSpecial.specials = new SpecialMarkers::Special[tempSpecials.size()];
421      for (U32 i = 0; i < tempSpecials.size(); i++) {
422         rSpecial.specials[i].start       = tempSpecials[i].start;
423         rSpecial.specials[i].end         = tempSpecials[i].end;
424         rSpecial.specials[i].specialType = tempTypes[i];
425      }
426   } else {
427      rSpecial.specials = NULL;
428   }
429}
430
431
432//--------------------------------------------------------------------------
433void GuiMessageVectorCtrl::createLineWrapping(LineWrapping& rWrapping, const char* string)
434{
435   Vector<TempLineBreak> tempBreaks(__FILE__, __LINE__);
436
437   U32 i;
438   U32 currStart = 0;
439   U32 length = dStrlen(string);
440   if (length != 0) {
441      for (i = 0; i < length; i++) {
442         if (string[i] == '\n') {
443            tempBreaks.increment();
444            tempBreaks.last().start = currStart;
445            tempBreaks.last().end   = i-1;
446            currStart = i+1;
447         } else if (i == length - 1) {
448            tempBreaks.increment();
449            tempBreaks.last().start = currStart;
450            tempBreaks.last().end   = i;
451            currStart = i+1;
452         }
453      }
454   } else {
455      tempBreaks.increment();
456      tempBreaks.last().start = 0;
457      tempBreaks.last().end   = -1;
458   }
459
460   U32 splitWidth = getWidth();
461   U32 currLine = 0;
462   while (currLine < tempBreaks.size()) {
463      TempLineBreak& rLine = tempBreaks[currLine];
464      if (rLine.start >= rLine.end) {
465         if (currLine == 0)
466            splitWidth -= mLineContinuationIndent;
467         currLine++;
468         continue;
469      }
470
471      // Ok, there's some actual text in this line.  How long is it?
472      U32 baseLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], rLine.end-rLine.start+1);
473      if (baseLength > splitWidth) {
474         // DMMNOTE: Replace with bin search eventually
475         U32 currPos = 0;
476         U32 breakPos = 0;
477         for (currPos = 0; currPos < rLine.end-rLine.start+1; currPos++) {
478            U32 currLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], currPos+1);
479            if (currLength > splitWidth) 
480            {
481               // Make sure that the currPos has advanced, then set the breakPoint.
482               breakPos = currPos != 0 ? currPos - 1 : 0;
483               break;
484            }
485         }
486         if (currPos == rLine.end-rLine.start+1) {
487            AssertFatal(false, "Error, if the line must be broken, the position must be before this point!");
488            currLine++;
489            continue;
490         }
491
492         // Ok, the character at breakPos is the last valid char we can render.  We
493         //  want to scan back to the first whitespace character (which, in the bounds
494         //  of the line, is guaranteed to be a space or a tab).
495         U32 originalBreak = breakPos;
496         while (true) {
497            if (string[rLine.start + breakPos] == ' ' || string[rLine.start + breakPos] == '\t') {
498               break;
499            } else {
500               AssertFatal(string[rLine.start + breakPos] != '\n',
501                           "Bad characters in line range...");
502               if (breakPos == 0) {
503                  breakPos = originalBreak;
504                  break;
505               }
506               breakPos--;
507            }
508         }
509
510         // Ok, everything up to and including breakPos is in the currentLine.  Insert
511         //  a new line at this point, and put everything after in that line.
512         S32 oldStart = rLine.start;
513         S32 oldEnd   = rLine.end;
514         rLine.end    = rLine.start + breakPos;
515
516         // Note that rLine is NOTNOTNOTNOT valid after this point!
517         tempBreaks.insert(currLine+1);
518         tempBreaks[currLine+1].start = oldStart + breakPos + 1;
519         tempBreaks[currLine+1].end   = oldEnd;
520      }
521
522      if (currLine == 0)
523         splitWidth -= mLineContinuationIndent;
524      currLine++;
525   }
526
527   rWrapping.numLines = tempBreaks.size();
528   rWrapping.startEndPairs = new LineWrapping::SEPair[tempBreaks.size()];
529   for (i = 0; i < tempBreaks.size(); i++) {
530      rWrapping.startEndPairs[i].start = tempBreaks[i].start;
531      rWrapping.startEndPairs[i].end   = tempBreaks[i].end;
532   }
533}
534
535//--------------------------------------------------------------------------
536void GuiMessageVectorCtrl::createLineElement(LineElement&    rElement,
537                                             LineWrapping&   rWrapping,
538                                             SpecialMarkers& rSpecial)
539{
540   // First, do a straighforward translation of the wrapping...
541   TextElement** ppWalk = &rElement.headLineElements;
542   for (U32 i = 0; i < rWrapping.numLines; i++) {
543      *ppWalk = new TextElement;
544
545      (*ppWalk)->nextInLine       = NULL;
546      (*ppWalk)->nextPhysicalLine = NULL;
547      (*ppWalk)->specialReference = -1;
548
549      (*ppWalk)->start = rWrapping.startEndPairs[i].start;
550      (*ppWalk)->end   = rWrapping.startEndPairs[i].end;
551
552      ppWalk = &((*ppWalk)->nextPhysicalLine);
553   }
554
555   if (rSpecial.numSpecials != 0) {
556      // Ok.  Now, walk down the lines, and split the elements into their constituent parts...
557      //
558      TextElement* walk = rElement.headLineElements;
559      while (walk) {
560         TextElement* walkAcross = walk;
561         while (walkAcross) {
562            S32 specialMatch = -1;
563            for (U32 i = 0; i < rSpecial.numSpecials; i++) {
564               if (walkAcross->start <= rSpecial.specials[i].end &&
565                   walkAcross->end   >= rSpecial.specials[i].start) {
566                  specialMatch = i;
567                  break;
568               }
569            }
570
571            if (specialMatch != -1) {
572               // We have a match here.  Break it into the appropriate number of segments.
573               //  this will vary depending on how the overlap is occuring...
574               if (walkAcross->start >= rSpecial.specials[specialMatch].start) {
575                  if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
576                     // The whole thing is part of the special
577                     AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
578                     walkAcross->specialReference = specialMatch;
579                  } else {
580                     // The first part is in the special, the tail is out
581                     AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
582                     walkAcross->nextInLine = new TextElement;
583                     walkAcross->nextInLine->nextInLine       = NULL;
584                     walkAcross->nextInLine->nextPhysicalLine = NULL;
585                     walkAcross->nextInLine->specialReference = -1;
586
587                     walkAcross->specialReference  = specialMatch;
588                     walkAcross->nextInLine->end   = walkAcross->end;
589                     walkAcross->end               = rSpecial.specials[specialMatch].end;
590                     walkAcross->nextInLine->start = rSpecial.specials[specialMatch].end + 1;
591
592                     AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
593                  }
594
595                  walkAcross = walkAcross->nextInLine;
596               } else {
597                  if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
598                     // The first part is out of the special, the second part in.
599                     AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
600                     walkAcross->nextInLine = new TextElement;
601                     walkAcross->nextInLine->nextInLine       = NULL;
602                     walkAcross->nextInLine->nextPhysicalLine = NULL;
603                     walkAcross->nextInLine->specialReference = specialMatch;
604
605                     walkAcross->specialReference  = -1;
606                     walkAcross->nextInLine->end   = walkAcross->end;
607                     walkAcross->end               = rSpecial.specials[specialMatch].start - 1;
608                     walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
609
610                     AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
611                     walkAcross = walkAcross->nextInLine;
612                  } else {
613                     // First out, middle in, last out.  Oy.
614                     AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
615                     walkAcross->nextInLine = new TextElement;
616                     walkAcross->nextInLine->nextInLine       = NULL;
617                     walkAcross->nextInLine->nextPhysicalLine = NULL;
618                     walkAcross->nextInLine->specialReference = specialMatch;
619
620                     walkAcross->nextInLine->nextInLine                   = new TextElement;
621                     walkAcross->nextInLine->nextInLine->nextInLine       = NULL;
622                     walkAcross->nextInLine->nextInLine->nextPhysicalLine = NULL;
623                     walkAcross->nextInLine->nextInLine->specialReference = -1;
624
625                     walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
626                     walkAcross->nextInLine->end   = rSpecial.specials[specialMatch].end;
627                     walkAcross->nextInLine->nextInLine->start = rSpecial.specials[specialMatch].end+1;
628                     walkAcross->nextInLine->nextInLine->end   = walkAcross->end;
629                     walkAcross->end = walkAcross->nextInLine->start - 1;
630                     AssertFatal((walkAcross->end >= walkAcross->start &&
631                                  walkAcross->nextInLine->end >= walkAcross->nextInLine->start &&
632                                  walkAcross->nextInLine->nextInLine->end >= walkAcross->nextInLine->nextInLine->start),
633                                 "Bad textelements generated!");
634                     walkAcross = walkAcross->nextInLine->nextInLine;
635                  }
636               }
637            } else {
638               walkAcross = walkAcross->nextInLine;
639            }
640         }
641
642         walk = walk->nextPhysicalLine;
643      }
644   }
645}
646
647
648//--------------------------------------------------------------------------
649bool GuiMessageVectorCtrl::onWake()
650{
651   if (Parent::onWake() == false)
652      return false;
653
654   if (bool(mProfile->mFont) == false)
655      return false;
656
657   mMinSensibleWidth = 1;
658
659   for (U32 i = 0; i < 256; i++) {
660      if (mProfile->mFont->isValidChar(U8(i))) {
661         if (mProfile->mFont->getCharWidth(U8(i)) > mMinSensibleWidth)
662            mMinSensibleWidth = mProfile->mFont->getCharWidth(U8(i));
663      }
664   }
665
666   AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
667   return true;
668}
669
670
671//--------------------------------------------------------------------------
672void GuiMessageVectorCtrl::onSleep()
673{
674   if (isAttached())
675      detach();
676
677   Parent::onSleep();
678}
679
680
681//--------------------------------------------------------------------------
682void GuiMessageVectorCtrl::onRender(Point2I      offset,
683                                    const RectI& updateRect)
684{
685   GFXDrawUtil *drawer = GFX->getDrawUtil();
686
687   Parent::onRender(offset, updateRect);
688   if (isAttached()) {
689      U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
690      U32 currLine   = 0;
691      ColorI lastColor = mProfile->mFontColor;
692      for (U32 i = 0; i < mMessageVector->getNumLines(); i++) {
693
694         TextElement* pElement = mLineElements[i].headLineElements;
695         while (pElement != NULL) {
696            Point2I localStart(pElement == mLineElements[i].headLineElements ? 0 : mLineContinuationIndent, currLine * linePixels);
697
698            Point2I globalCheck  = localToGlobalCoord(localStart);
699            U32 globalRangeStart = globalCheck.y;
700            U32 globalRangeEnd   = globalCheck.y + mProfile->mFont->getHeight();
701            if (globalRangeStart > updateRect.point.y + updateRect.extent.y ||
702                globalRangeEnd   < updateRect.point.y) {
703               currLine++;
704               pElement = pElement->nextPhysicalLine;
705               continue;
706            }
707
708            TextElement* walkAcross = pElement;
709            while (walkAcross) {
710               if (walkAcross->start > walkAcross->end)
711                  break;
712
713               Point2I globalStart  = localToGlobalCoord(localStart);
714
715               U32 strWidth;
716               if (walkAcross->specialReference == -1) {
717                  drawer->setBitmapModulation(lastColor);
718                  drawer->setTextAnchorColor(mProfile->mFontColor);
719                  strWidth = drawer->drawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
720                                          walkAcross->end - walkAcross->start + 1, mProfile->mFontColors, mMaxColorIndex);
721                  drawer->getBitmapModulation(&lastColor);  // in case an embedded color tag changed it
722               } else {
723                  drawer->getBitmapModulation( &lastColor );
724                  drawer->setBitmapModulation(mSpecialColor);
725                  drawer->setTextAnchorColor(mProfile->mFontColor);
726                  strWidth = drawer->drawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
727                                          walkAcross->end - walkAcross->start + 1);
728
729                  // in case we have 2 in a row...
730                  drawer->setBitmapModulation(lastColor);
731               }
732
733               // drawTextN returns the rightmost X coord, so subtract leftmost coord to get the width
734               strWidth -= globalStart.x;
735
736               if (walkAcross->specialReference != -1) {
737                  Point2I lineStart = localStart;
738                  Point2I lineEnd   = localStart;
739                  lineStart.y += mProfile->mFont->getBaseline() + 1;
740                  lineEnd.x += strWidth;
741                  lineEnd.y += mProfile->mFont->getBaseline() + 1;
742
743                  drawer->drawLine(localToGlobalCoord(lineStart),
744                              localToGlobalCoord(lineEnd),
745                              mSpecialColor);
746               }
747
748               localStart.x += strWidth;
749               walkAcross = walkAcross->nextInLine;
750            }
751
752            currLine++;
753            pElement = pElement->nextPhysicalLine;
754         }
755      }
756      drawer->clearBitmapModulation();
757   }
758}
759
760
761//--------------------------------------------------------------------------
762void GuiMessageVectorCtrl::inspectPostApply()
763{
764   Parent::inspectPostApply();
765}
766
767
768//--------------------------------------------------------------------------
769void GuiMessageVectorCtrl::parentResized(const RectI& oldParentRect, const RectI& newParentRect)
770{
771   Parent::parentResized(oldParentRect, newParentRect);
772
773   // If we have a MesssageVector, detach/reattach so we can reflow the text.
774   if (mMessageVector)
775   {
776      MessageVector *reflowme = mMessageVector;
777
778      detach();
779      attach(reflowme);
780   }
781}
782
783//--------------------------------------------------------------------------
784void GuiMessageVectorCtrl::findSpecialFromCoord(const Point2I& point, S32* specialLine, S32* specialRef)
785{
786   if (mLineElements.size() == 0) {
787      *specialLine = -1;
788      *specialRef  = -1;
789      return;
790   }
791
792   U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
793   
794   if ((point.x < 0 || point.x >= getWidth()) ||
795       (point.y < 0 || point.y >= getHeight())) {
796      *specialLine = -1;
797      *specialRef  = -1;
798      return;
799   }
800
801   // Ok, we have real work to do here.  Let's determine the physical line that it's on...
802   U32 physLine = point.y / linePixels;
803   AssertFatal(physLine >= 0, "Bad physical line!");
804
805   // And now we find the LineElement that contains that physicalLine...
806   U32 elemIndex;
807   for (elemIndex = 0; elemIndex < mLineElements.size(); elemIndex++) {
808      if (mLineElements[elemIndex].physicalLineStart > physLine) {
809         // We've passed it.
810         AssertFatal(elemIndex != 0, "Error, bad elemIndex, check assumptions.");
811         elemIndex--;
812         break;
813      }
814   }
815   if (elemIndex == mLineElements.size()) {
816      // On the last line...
817      elemIndex = mLineElements.size() - 1;
818   }
819
820   TextElement* line = mLineElements[elemIndex].headLineElements;
821   for (U32 i = 0; i < physLine - mLineElements[elemIndex].physicalLineStart; i++)
822   {
823      if (line->nextPhysicalLine == NULL)
824      {
825         *specialLine = -1;
826         *specialRef  = -1;
827         return;
828      }
829      line = line->nextPhysicalLine;
830      AssertFatal(line != NULL, "Error, moved too far!");
831   }
832
833   // Ok, line represents the current line.  We now need to find out which textelement
834   //  the points x coord falls in.
835   U32 currX = 0;
836   if ((physLine - mLineElements[elemIndex].physicalLineStart) != 0) {
837      currX = mLineContinuationIndent;
838      // First, if this isn't the first line in this wrapping, we have to make sure
839      //  that the coord isn't in the margin...
840      if (point.x < mLineContinuationIndent) {
841         *specialLine = -1;
842         *specialRef  = -1;
843         return;
844      }
845   }
846   if (line->start > line->end) {
847      // Empty line special case...
848      *specialLine = -1;
849      *specialRef  = -1;
850      return;
851   }
852
853   while (line) {
854      U32 newX = currX + mProfile->mFont->getStrNWidth((const UTF8 *)mMessageVector->getLine(elemIndex).message,
855                                                       line->end - line->start + 1);
856      if (point.x < newX) {
857         // That's the one!
858         *specialLine = elemIndex;
859         *specialRef  = line->specialReference;
860         return;
861      }
862
863      currX = newX;
864      line = line->nextInLine;
865   }
866
867   // Off to the right.  Aw...
868   *specialLine = -1;
869   *specialRef  = -1;
870}
871
872
873void GuiMessageVectorCtrl::onMouseDown(const GuiEvent& event)
874{
875   Parent::onMouseDown(event);
876   mouseUnlock();
877
878   mMouseDown = true;
879
880   // Find the special we are in, if any...
881   findSpecialFromCoord(globalToLocalCoord(event.mousePoint),
882                        &mMouseSpecialLine, &mMouseSpecialRef);
883}
884
885void GuiMessageVectorCtrl::onMouseUp(const GuiEvent& event)
886{
887   Parent::onMouseUp(event);
888   mouseUnlock();
889
890   // Is this an up from a dragged click?
891   if (mMouseDown == false)
892      return;
893
894   // Find the special we are in, if any...
895
896   S32 currSpecialLine;
897   S32 currSpecialRef;
898   findSpecialFromCoord(globalToLocalCoord(event.mousePoint), &currSpecialLine, &currSpecialRef);
899
900   if (currSpecialRef != -1 &&
901       (currSpecialLine == mMouseSpecialLine &&
902        currSpecialRef  == mMouseSpecialRef)) {
903      // Execute the callback
904      SpecialMarkers& rSpecial = mSpecialMarkers[currSpecialLine];
905      S32 specialStart = rSpecial.specials[currSpecialRef].start;
906      S32 specialEnd   = rSpecial.specials[currSpecialRef].end;
907
908      char* copyURL = new char[specialEnd - specialStart + 2];
909      dStrncpy(copyURL, &mMessageVector->getLine(currSpecialLine).message[specialStart], specialEnd - specialStart + 1);
910      copyURL[specialEnd - specialStart + 1] = '\0';
911
912      Con::executef(this, "urlClickCallback", copyURL);
913      delete [] copyURL;
914   }
915
916   mMouseDown      = false;
917   mMouseSpecialLine = -1;
918   mMouseSpecialRef  = -1;
919}
920
921