123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "gui/utility/messageVector.h"
- #include "core/fileObject.h"
- #include "console/engineAPI.h"
- IMPLEMENT_CONOBJECT(MessageVector);
- ConsoleDocClass( MessageVector,
- "@brief Store a list of chat messages.\n\n"
- "This is responsible for managing messages which appear in the chat HUD, not the actual control rendered to the screen\n\n"
-
- "@tsexample\n"
- "// Declare ChatHud, which is what will display the actual chat from a MessageVector\n"
- "new GuiMessageVectorCtrl(ChatHud) {\n"
- " profile = \"ChatHudMessageProfile\";\n"
- " horizSizing = \"width\";\n"
- " vertSizing = \"height\";\n"
- " position = \"1 1\";\n"
- " extent = \"252 16\";\n"
- " minExtent = \"8 8\";\n"
- " visible = \"1\";\n"
- " helpTag = \"0\";\n"
- " lineSpacing = \"0\";\n"
- " lineContinuedIndex = \"10\";\n"
- " matchColor = \"0 0 255 255\";\n"
- " maxColorIndex = \"5\";\n"
- "};\n\n"
- "// All messages are stored in this HudMessageVector, the actual\n"
- "// MainChatHud only displays the contents of this vector.\n"
- "new MessageVector(HudMessageVector);\n\n"
- "// Attach the MessageVector to the chat control\n"
- "chatHud.attach(HudMessageVector);\n"
- "@endtsexample\n\n"
- "@see GuiMessageVectorCtrl for more details on how this is used."
- "@ingroup GuiUtil\n"
- );
- DefineEngineMethod( MessageVector, clear, void, (),,
- "Clear all messages in the vector\n\n"
- "@tsexample\n"
- "HudMessageVector.clear();\n"
- "@endtsexample\n\n")
- {
- object->clear();
- }
- //ConsoleMethod( MessageVector, clear, void, 2, 2, "Clear the message vector.")
- //{
- // object->clear();
- //}
- DefineEngineMethod( MessageVector, pushBackLine, void, ( const char* msg, S32 tag ),,
- "Push a line onto the back of the list.\n\n"
- "@param msg Text that makes up the message\n"
- "@param tag Numerical value associated with this message, useful for searching.\n\n"
- "@tsexample\n"
- "// Add the message...\n"
- "HudMessageVector.pushBackLine(\"Hello World\", 0);\n"
- "@endtsexample\n\n")
- {
- object->pushBackLine(msg, tag);
- }
- //ConsoleMethod( MessageVector, pushBackLine, void, 3, 4, "(string msg, int tag=0)"
- // "Push a line onto the back of the list.")
- //{
- // U32 tag = 0;
- // if (argc == 4)
- // tag = dAtoi(argv[3]);
- //
- // object->pushBackLine(argv[2], tag);
- //}
- DefineEngineMethod( MessageVector, popBackLine, bool, (),,
- "Pop a line from the back of the list; destroys the line.\n\n"
- "@tsexample\n"
- "HudMessageVector.popBackLine();\n"
- "@endtsexample\n\n"
- "@return False if there are no lines to pop (underflow), true otherwise")
- {
- if (object->getNumLines() == 0) {
- Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow");
- return false;
- }
- object->popBackLine();
- return true;
- }
- //ConsoleMethod( MessageVector, popBackLine, bool, 2, 2, "()"
- // "Pop a line from the back of the list; destroys the line.")
- //{
- // if (object->getNumLines() == 0) {
- // Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow");
- // return false;
- // }
- //
- // object->popBackLine();
- // return true;
- //}
- DefineEngineMethod( MessageVector, pushFrontLine, void, ( const char* msg, S32 tag ),,
- "Push a line onto the front of the vector.\n\n"
- "@param msg Text that makes up the message\n"
- "@param tag Numerical value associated with this message, useful for searching.\n\n"
- "@tsexample\n"
- "// Add the message...\n"
- "HudMessageVector.pushFrontLine(\"Hello World\", 0);\n"
- "@endtsexample\n\n")
- {
- object->pushFrontLine(msg, tag);
- }
- //ConsoleMethod( MessageVector, pushFrontLine, void, 3, 4, "(string msg, int tag=0)"
- // "Push a line onto the front of the vector.")
- //{
- // U32 tag = 0;
- // if (argc == 4)
- // tag = dAtoi(argv[3]);
- //
- // object->pushFrontLine(argv[2], tag);
- //}
- DefineEngineMethod( MessageVector, popFrontLine, bool, (),,
- "Pop a line from the front of the vector, destroying the line.\n\n"
- "@tsexample\n"
- "HudMessageVector.popFrontLine();\n"
- "@endtsexample\n\n"
- "@return False if there are no lines to pop (underflow), true otherwise")
- {
- if (object->getNumLines() == 0) {
- Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow");
- return false;
- }
- object->popFrontLine();
- return true;
- }
- //ConsoleMethod( MessageVector, popFrontLine, bool, 2, 2,
- // "Pop a line from the front of the vector, destroying the line.")
- //{
- // if (object->getNumLines() == 0) {
- // Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow");
- // return false;
- // }
- //
- // object->popFrontLine();
- // return true;
- //}
- DefineEngineMethod( MessageVector, insertLine, bool, ( S32 insertPos, const char* msg, S32 tag ),,
- "Push a line onto the back of the list.\n\n"
- "@param msg Text that makes up the message\n"
- "@param tag Numerical value associated with this message, useful for searching.\n\n"
- "@tsexample\n"
- "// Add the message...\n"
- "HudMessageVector.insertLine(1, \"Hello World\", 0);\n"
- "@endtsexample\n\n"
- "@return False if insertPos is greater than the number of lines in the current vector")
- {
- if (insertPos > object->getNumLines())
- return false;
- object->insertLine(insertPos, msg, tag);
- return true;
- }
- //ConsoleMethod( MessageVector, insertLine, bool, 4, 5, "(int insertPos, string msg, int tag=0)"
- // "Insert a new line into the vector at the specified position.")
- //{
- // U32 pos = U32(dAtoi(argv[2]));
- // if (pos > object->getNumLines())
- // return false;
- //
- // S32 tag = 0;
- // if (argc == 5)
- // tag = dAtoi(argv[4]);
- //
- // object->insertLine(pos, argv[3], tag);
- // return true;
- //}
- DefineEngineMethod( MessageVector, deleteLine, bool, ( S32 deletePos),,
- "Delete the line at the specified position.\n\n"
- "@param deletePos Position in the vector containing the line to be deleted\n"
- "@tsexample\n"
- "// Delete the first line (index 0) in the vector...\n"
- "HudMessageVector.deleteLine(0);\n"
- "@endtsexample\n\n"
- "@return False if deletePos is greater than the number of lines in the current vector")
- {
- if (deletePos >= object->getNumLines())
- return false;
- object->deleteLine(deletePos);
- return true;
- }
- //ConsoleMethod( MessageVector, deleteLine, bool, 3, 3, "(int deletePos)"
- // "Delete the line at the specified position.")
- //{
- // U32 pos = U32(dAtoi(argv[2]));
- // if (pos >= object->getNumLines())
- // return false;
- //
- // object->deleteLine(pos);
- // return true;
- //}
- static ConsoleDocFragment _MessageVectordump1(
- "@brief Dump the message vector to a file without a header.\n\n"
-
- "@param filename Name and path of file to dump text to.\n"
-
- "@tsexample\n"
- "// Dump the entire chat log to a text file\n"
- "HudMessageVector.dump(\"./chatLog.txt\");\n"
- "@endtsexample\n\n\n",
- "MessageVector",
- "void dump( string filename);");
- static ConsoleDocFragment _MessageVectordump2(
- "@brief Dump the message vector to a file with a header.\n\n"
-
- "@param filename Name and path of file to dump text to.\n"
- "@param header Prefix information for write out\n\n"
-
- "@tsexample\n"
- "// Arbitrary header data\n"
- "%headerInfo = \"Ars Moriendi Chat Log\";\n\n"
- "// Dump the entire chat log to a text file\n"
- "HudMessageVector.dump(\"./chatLog.txt\", %headerInfo);\n"
- "@endtsexample\n\n\n",
- "MessageVector",
- "void dump( string filename, string header);");
- DefineEngineMethod( MessageVector, dump, void, (const char * filename, const char * header), (""), "(string filename, string header=NULL)"
- "Dump the message vector to a file, optionally prefixing a header."
- "@hide")
- {
- object->dump( filename, header );
- }
- DefineEngineMethod( MessageVector, getNumLines, S32, (),,
- "Get the number of lines in the vector.\n\n"
- "@tsexample\n"
- "// Find out how many lines have been stored in HudMessageVector\n"
- "%chatLines = HudMessageVector.getNumLines();\n"
- "echo(%chatLines);\n"
- "@endtsexample\n\n")
- {
- return object->getNumLines();
- }
- //ConsoleMethod( MessageVector, getNumLines, S32, 2, 2, "Get the number of lines in the vector.")
- //{
- // return object->getNumLines();
- //}
- DefineEngineMethod( MessageVector, getLineTextByTag, const char*, ( S32 tag),,
- "Scan through the lines in the vector, returning the first line that has a matching tag.\n\n"
- "@param tag Numerical value assigned to a message when it was added or inserted\n"
- "@tsexample\n"
- "// Locate text in the vector tagged with the value \"1\", then print it\n"
- "%taggedText = HudMessageVector.getLineTextByTag(1);\n"
- "echo(%taggedText);\n"
- "@endtsexample\n\n"
- "@return Text from a line with matching tag, other wise \"\"")
- {
- for(U32 i = 0; i < object->getNumLines(); i++)
- if(object->getLine(i).messageTag == tag)
- return object->getLine(i).message;
- return "";
- }
- //ConsoleMethod( MessageVector, getLineTextByTag, const char*, 3, 3, "(int tag)"
- // "Scan through the lines in the vector, returning the first line that has a matching tag.")
- //{
- // U32 tag = dAtoi(argv[2]);
- //
- // for(U32 i = 0; i < object->getNumLines(); i++)
- // if(object->getLine(i).messageTag == tag)
- // return object->getLine(i).message;
- // return "";
- //}
- DefineEngineMethod( MessageVector, getLineIndexByTag, S32, ( S32 tag),,
- "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.\n\n"
- "@param tag Numerical value assigned to a message when it was added or inserted\n"
- "@tsexample\n"
- "// Locate a line of text tagged with the value \"1\", then delete it.\n"
- "%taggedLine = HudMessageVector.getLineIndexByTag(1);\n"
- "HudMessageVector.deleteLine(%taggedLine);\n"
- "@endtsexample\n\n"
- "@return Line with matching tag, other wise -1")
- {
- for(U32 i = 0; i < object->getNumLines(); i++)
- if(object->getLine(i).messageTag == tag)
- return i;
- return -1;
- }
- //ConsoleMethod( MessageVector, getLineIndexByTag, S32, 3, 3, "(int tag)"
- // "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.")
- //{
- // U32 tag = dAtoi(argv[2]);
- //
- // for(U32 i = 0; i < object->getNumLines(); i++)
- // if(object->getLine(i).messageTag == tag)
- // return i;
- // return -1;
- //}
- DefineEngineMethod( MessageVector, getLineText, const char*, ( S32 pos),,
- "Get the text at a specified line.\n\n"
- "@param pos Position in vector to grab text from\n"
- "@tsexample\n"
- "// Print a line of text at position 1.\n"
- "%text = HudMessageVector.getLineText(1);\n"
- "echo(%text);\n"
- "@endtsexample\n\n"
- "@return Text at specified line, if the position is greater than the number of lines return \"\"")
- {
- if (pos >= object->getNumLines()) {
- Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line");
- return "";
- }
- return object->getLine(pos).message;
- }
- //ConsoleMethod( MessageVector, getLineText, const char*, 3, 3, "(int line)"
- // "Get the text at a specified line.")
- //{
- // U32 pos = U32(dAtoi(argv[2]));
- // if (pos >= object->getNumLines()) {
- // Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line");
- // return "";
- // }
- //
- // return object->getLine(pos).message;
- //}
- DefineEngineMethod( MessageVector, getLineTag, S32, ( S32 pos),,
- "Get the tag of a specified line.\n\n"
- "@param pos Position in vector to grab tag from\n"
- "@tsexample\n"
- "// Remove all lines that do not have a tag value of 1.\n"
- "while( HudMessageVector.getNumLines())\n"
- "{\n"
- " %tag = HudMessageVector.getLineTag(1);\n"
- " if(%tag != 1)\n"
- " %tag.delete();\n"
- " HudMessageVector.popFrontLine();\n"
- "}\n"
- "@endtsexample\n\n"
- "@return Tag value of a given line, if the position is greater than the number of lines return 0")
- {
- if (pos >= object->getNumLines()) {
- Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line");
- return 0;
- }
- return object->getLine(pos).messageTag;
- }
- //ConsoleMethod( MessageVector, getLineTag, S32, 3, 3, "(int line)"
- // "Get the tag of a specified line.")
- //{
- // U32 pos = U32(dAtoi(argv[2]));
- // if (pos >= object->getNumLines()) {
- // Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line");
- // return 0;
- // }
- //
- // return object->getLine(pos).messageTag;
- //}
- //--------------------------------------------------------------------------
- MessageVector::MessageVector()
- {
- VECTOR_SET_ASSOCIATION(mMessageLines);
- VECTOR_SET_ASSOCIATION(mSpectators);
- }
- //--------------------------------------------------------------------------
- MessageVector::~MessageVector()
- {
- for (U32 i = 0; i < mMessageLines.size(); i++) {
- char* pDelete = const_cast<char*>(mMessageLines[i].message);
- delete [] pDelete;
- mMessageLines[i].message = 0;
- mMessageLines[i].messageTag = 0xFFFFFFFF;
- }
- mMessageLines.clear();
- }
- //--------------------------------------------------------------------------
- void MessageVector::initPersistFields()
- {
- Parent::initPersistFields();
- }
- //--------------------------------------------------------------------------
- bool MessageVector::onAdd()
- {
- return Parent::onAdd();
- }
- //--------------------------------------------------------------------------
- void MessageVector::onRemove()
- {
- // Delete all the lines from the observers, and then forcibly detatch ourselves
- //
- for (S32 i = mMessageLines.size() - 1; i >= 0; i--)
- spectatorMessage(LineDeleted, i);
- spectatorMessage(VectorDeletion, 0);
- mSpectators.clear();
- Parent::onRemove();
- }
- //--------------------------------------------------------------------------
- void MessageVector::pushBackLine(const char* newMessage, const S32 newMessageTag)
- {
- insertLine(mMessageLines.size(), newMessage, newMessageTag);
- }
- void MessageVector::popBackLine()
- {
- AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
- if (mMessageLines.size() == 0)
- return;
- deleteLine(mMessageLines.size() - 1);
- }
- void MessageVector::clear()
- {
- while(mMessageLines.size())
- deleteLine(mMessageLines.size() - 1);
- }
- //--------------------------------------------------------------------------
- void MessageVector::pushFrontLine(const char* newMessage, const S32 newMessageTag)
- {
- insertLine(0, newMessage, newMessageTag);
- }
- void MessageVector::popFrontLine()
- {
- AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
- if (mMessageLines.size() == 0)
- return;
- deleteLine(0);
- }
- //--------------------------------------------------------------------------
- void MessageVector::insertLine(const U32 position,
- const char* newMessage,
- const S32 newMessageTag)
- {
- AssertFatal(position >= 0 && position <= mMessageLines.size(), "MessageVector::insertLine: out of range position!");
- AssertFatal(newMessage != NULL, "Error, no message to insert!");
- U32 len = dStrlen(newMessage) + 1;
- char* copy = new char[len];
- dStrcpy(copy, newMessage, len);
- mMessageLines.insert(position);
- mMessageLines[position].message = copy;
- mMessageLines[position].messageTag = newMessageTag;
- // Notify of insert
- spectatorMessage(LineInserted, position);
- }
- //--------------------------------------------------------------------------
- void MessageVector::deleteLine(const U32 position)
- {
- AssertFatal(position >= 0 && position < mMessageLines.size(), "MessageVector::deleteLine: out of range position!");
- char* pDelete = const_cast<char*>(mMessageLines[position].message);
- delete [] pDelete;
- mMessageLines[position].message = NULL;
- mMessageLines[position].messageTag = 0xFFFFFFFF;
- mMessageLines.erase(position);
- // Notify of delete
- spectatorMessage(LineDeleted, position);
- }
- //--------------------------------------------------------------------------
- bool MessageVector::dump( const char* filename, const char* header )
- {
- Con::printf( "Dumping message vector %s to %s...", getName(), filename );
- FileObject file;
- if ( !file.openForWrite( filename ) )
- return( false );
- // If passed a header line, write it out first:
- if ( header )
- file.writeLine( (const U8*) header );
- // First write out the record count:
- char* lineBuf = (char*) dMalloc( 10 );
- dSprintf( lineBuf, 10, "%d", mMessageLines.size() );
- file.writeLine( (const U8*) lineBuf );
- // Write all of the lines of the message vector:
- U32 len;
- for ( U32 i = 0; i < mMessageLines.size(); i++ )
- {
- len = ( dStrlen( mMessageLines[i].message ) * 2 ) + 10;
- lineBuf = (char*) dRealloc( lineBuf, len );
- dSprintf( lineBuf, len, "%d ", mMessageLines[i].messageTag );
- expandEscape( lineBuf + dStrlen( lineBuf ), mMessageLines[i].message );
- file.writeLine( (const U8*) lineBuf );
- }
- file.close();
- return( true );
- }
- //--------------------------------------------------------------------------
- void MessageVector::registerSpectator(SpectatorCallback callBack, void *spectatorKey)
- {
- AssertFatal(callBack != NULL, "Error, must have a callback!");
- // First, make sure that this hasn't been registered already...
- U32 i;
- for (i = 0; i < mSpectators.size(); i++) {
- AssertFatal(mSpectators[i].key != spectatorKey, "Error, spectator key already registered!");
- if (mSpectators[i].key == spectatorKey)
- return;
- }
- mSpectators.increment();
- SpectatorRef& lastSpectatorRef = mSpectators.last();
- lastSpectatorRef.callback = callBack;
- lastSpectatorRef.key = spectatorKey;
- // Need to message this spectator of all the lines currently inserted...
- for (i = 0; i < mMessageLines.size(); i++) {
- (*lastSpectatorRef.callback)(lastSpectatorRef.key,
- LineInserted, i);
- }
- }
- void MessageVector::unregisterSpectator(void * spectatorKey)
- {
- for (U32 i = 0; i < mSpectators.size(); i++) {
- if (mSpectators[i].key == spectatorKey) {
- // Need to message this spectator of all the lines currently inserted...
- for (S32 j = mMessageLines.size() - 1; j >= 0 ; j--) {
- (*mSpectators[i].callback)(mSpectators[i].key,
- LineDeleted, j);
- }
- mSpectators.erase(i);
- return;
- }
- }
- AssertFatal(false, "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
- Con::errorf(ConsoleLogEntry::General,
- "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
- }
- void MessageVector::spectatorMessage(MessageCode code, const U32 arg)
- {
- for (U32 i = 0; i < mSpectators.size(); i++) {
- (*mSpectators[i].callback)(mSpectators[i].key,
- code, arg);
- }
- }
|