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