messageVector.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gui/messageVector.h"
  23. #include "io/fileObject.h"
  24. IMPLEMENT_CONOBJECT(MessageVector);
  25. //-------------------------------------- Console functions
  26. ConsoleMethod( MessageVector, clear, void, 2, 2, "() Clear the message vector.\n"
  27. "@return No return value")
  28. {
  29. object->clear();
  30. }
  31. ConsoleMethod( MessageVector, pushBackLine, void, 4, 4, "( msg [ , tag ] ) Use the pushBackLine method to push a line onto the back of the list.\n"
  32. "@param msg The text to add to this control.\n"
  33. "@param tag An optional tag to tag this line with. If not tag is supplied, a tag of 0 is used.\n"
  34. "@return No return value.\n"
  35. "@sa popBackLine, popFrontLine, insertLine, pushFrontLine")
  36. {
  37. U32 tag = 0;
  38. if (argc == 4)
  39. tag = dAtoi(argv[3]);
  40. object->pushBackLine(argv[2], tag);
  41. }
  42. ConsoleMethod( MessageVector, popBackLine, bool, 2, 2, "() Use the popBackLine method to pop a line from the back of the list; destroys the line.\n"
  43. "@return No return value.\n"
  44. "@sa insertLine, pushBackLine, pushFrontLine")
  45. {
  46. if (object->getNumLines() == 0) {
  47. Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow");
  48. return false;
  49. }
  50. object->popBackLine();
  51. return true;
  52. }
  53. ConsoleMethod( MessageVector, pushFrontLine, void, 3, 4, "( msg [ , tag ] ) Use the pushFrontLine method to push a line onto the front of the vector.\n"
  54. "@param msg The text to add to this control.\n"
  55. "@param tag An optional tag to tag this line with. If not tag is supplied, a tag of 0 is used.\n"
  56. "@return No return value.\n"
  57. "@sa popBackLine, popFrontLine, insertLine, pushBackLine")
  58. {
  59. U32 tag = 0;
  60. if (argc == 4)
  61. tag = dAtoi(argv[3]);
  62. object->pushFrontLine(argv[2], tag);
  63. }
  64. ConsoleMethod( MessageVector, popFrontLine, bool, 2, 2,"() Use the popFrontLine method to pop a line from the front of the vector, destroying the line.\n"
  65. "@return No return value.\n"
  66. "@sa insertLine, pushBackLine, pushFrontLine")
  67. {
  68. if (object->getNumLines() == 0) {
  69. Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow");
  70. return false;
  71. }
  72. object->popFrontLine();
  73. return true;
  74. }
  75. ConsoleMethod( MessageVector, insertLine, bool, 4, 5, "( pos , msg [ , tag ] ) Use the insertLine method to insert a new line into the vector at the specified position. An optional tag may also be applied.\n"
  76. "@param pos The line at which to insert the new text.\n"
  77. "@param msg The text to add to this control.\n"
  78. "@param tag An optional tag to tag this line with. If not tag is supplied, a tag of 0 is used.\n"
  79. "@return No return value.\n"
  80. "@sa pushBackLine, pushFrontLine")
  81. {
  82. U32 pos = U32(dAtoi(argv[2]));
  83. if (pos > object->getNumLines())
  84. return false;
  85. S32 tag = 0;
  86. if (argc == 5)
  87. tag = dAtoi(argv[4]);
  88. object->insertLine(pos, argv[3], tag);
  89. return true;
  90. }
  91. ConsoleMethod( MessageVector, deleteLine, bool, 3, 3, "( lineIndex ) Delete the line at the specified position.\n"
  92. "@param lineIndex The line to delete in this vector.\n"
  93. "@return No return value.\n"
  94. "@sa insertLine, pushBackLine, pushFrontLine")
  95. {
  96. U32 pos = U32(dAtoi(argv[2]));
  97. if (pos >= object->getNumLines())
  98. return false;
  99. object->deleteLine(pos);
  100. return true;
  101. }
  102. ConsoleMethod( MessageVector, dump, void, 3, 4, "( filename [ , header ] ) Dump the message vector to a file, optionally prefixing the file with a header.\n"
  103. "@param filename The file to dump this vector to.\n"
  104. "@param header An optional string containing data to dump to the new file prior to dumping the vector.\n"
  105. "@return No return value")
  106. {
  107. if ( argc == 4 )
  108. object->dump( argv[2], argv[3] );
  109. else
  110. object->dump( argv[2] );
  111. }
  112. ConsoleMethod( MessageVector, getNumLines, S32, 2, 2, "() Use the getNumLines method to get the number of lines in the vector.\n"
  113. "@return Returns an integer value equal to the line count for this vector.\n"
  114. "@sa insertLine, pushBackLine, pushFrontLine")
  115. {
  116. return object->getNumLines();
  117. }
  118. ConsoleMethod( MessageVector, getLineTextByTag, const char*, 3, 3, "( tag ) Use the getLineTextByTag method to scan through the lines in the vector, returning the first line that has a matching tag.\n"
  119. "@param tag An special marker that may have been used when creating lines in the vector.\n"
  120. "@return Returns the contents of the first line found with a matching tag or NULL indicating no match.\n"
  121. "@sa insertLine, pushBackLine, pushFrontLine")
  122. {
  123. U32 tag = dAtoi(argv[2]);
  124. for(U32 i = 0; i < object->getNumLines(); i++)
  125. if(object->getLine(i).messageTag == tag)
  126. return object->getLine(i).message;
  127. return "";
  128. }
  129. ConsoleMethod( MessageVector, getLineIndexByTag, S32, 3, 3, "( 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"
  130. "@param tag A special marker, possibly embedded in one or more lines in the vector.\n"
  131. "@return Returns the line number of the first line found with the specified tag, otherwise returns -1.\n"
  132. "@sa insertLine, pushBackLine, pushFrontLine")
  133. {
  134. U32 tag = dAtoi(argv[2]);
  135. for(U32 i = 0; i < object->getNumLines(); i++)
  136. if(object->getLine(i).messageTag == tag)
  137. return i;
  138. return -1;
  139. }
  140. ConsoleMethod( MessageVector, getLineText, const char*, 3, 3, "( index ) Use the getLineIndex method to get the text at a specified line.\n"
  141. "@param index The index in the vector from which to retrieve a line of text.\n"
  142. "@return Returns the text at the specified line, or NULL indicating a bad index.\n"
  143. "@sa insertLine, pushBackLine, pushFrontLine")
  144. {
  145. U32 pos = U32(dAtoi(argv[2]));
  146. if (pos >= object->getNumLines()) {
  147. Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line");
  148. return "";
  149. }
  150. return object->getLine(pos).message;
  151. }
  152. ConsoleMethod( MessageVector, getLineTag, S32, 3, 3, "(line) Use the getLineTag method to retrieve the tag for the specified line.\n"
  153. "@param line Line to search for tag in.\n"
  154. "@return Returns a tag value or 0 indicating no tag found.\n"
  155. "@sa insertLine, pushBackLine, pushFrontLine")
  156. {
  157. U32 pos = U32(dAtoi(argv[2]));
  158. if (pos >= object->getNumLines()) {
  159. Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line");
  160. return 0;
  161. }
  162. return object->getLine(pos).messageTag;
  163. }
  164. //--------------------------------------------------------------------------
  165. MessageVector::MessageVector()
  166. {
  167. VECTOR_SET_ASSOCIATION(mMessageLines);
  168. VECTOR_SET_ASSOCIATION(mSpectators);
  169. }
  170. //--------------------------------------------------------------------------
  171. MessageVector::~MessageVector()
  172. {
  173. for (U32 i = 0; i < (U32)mMessageLines.size(); i++) {
  174. char* pDelete = const_cast<char*>(mMessageLines[i].message);
  175. delete [] pDelete;
  176. mMessageLines[i].message = 0;
  177. mMessageLines[i].messageTag = 0xFFFFFFFF;
  178. }
  179. mMessageLines.clear();
  180. }
  181. //--------------------------------------------------------------------------
  182. void MessageVector::initPersistFields()
  183. {
  184. Parent::initPersistFields();
  185. }
  186. //--------------------------------------------------------------------------
  187. bool MessageVector::onAdd()
  188. {
  189. return Parent::onAdd();
  190. }
  191. //--------------------------------------------------------------------------
  192. void MessageVector::onRemove()
  193. {
  194. // Delete all the lines from the observers, and then forcibly detatch ourselves
  195. //
  196. for (S32 i = mMessageLines.size() - 1; i >= 0; i--)
  197. spectatorMessage(LineDeleted, i);
  198. spectatorMessage(VectorDeletion, 0);
  199. mSpectators.clear();
  200. Parent::onRemove();
  201. }
  202. //--------------------------------------------------------------------------
  203. void MessageVector::pushBackLine(const char* newMessage, const S32 newMessageTag)
  204. {
  205. insertLine(mMessageLines.size(), newMessage, newMessageTag);
  206. }
  207. void MessageVector::popBackLine()
  208. {
  209. AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
  210. if (mMessageLines.size() == 0)
  211. return;
  212. deleteLine(mMessageLines.size() - 1);
  213. }
  214. void MessageVector::clear()
  215. {
  216. while(mMessageLines.size())
  217. deleteLine(mMessageLines.size() - 1);
  218. }
  219. //--------------------------------------------------------------------------
  220. void MessageVector::pushFrontLine(const char* newMessage, const S32 newMessageTag)
  221. {
  222. insertLine(0, newMessage, newMessageTag);
  223. }
  224. void MessageVector::popFrontLine()
  225. {
  226. AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
  227. if (mMessageLines.size() == 0)
  228. return;
  229. deleteLine(0);
  230. }
  231. //--------------------------------------------------------------------------
  232. void MessageVector::insertLine(const U32 position,
  233. const char* newMessage,
  234. const S32 newMessageTag)
  235. {
  236. AssertFatal(position >= 0 && position <= (U32)mMessageLines.size(), "MessageVector::insertLine: out of range position!");
  237. AssertFatal(newMessage != NULL, "Error, no message to insert!");
  238. U32 len = dStrlen(newMessage) + 1;
  239. char* copy = new char[len];
  240. dStrcpy(copy, newMessage);
  241. mMessageLines.insert(position);
  242. mMessageLines[position].message = copy;
  243. mMessageLines[position].messageTag = newMessageTag;
  244. // Notify of insert
  245. spectatorMessage(LineInserted, position);
  246. }
  247. //--------------------------------------------------------------------------
  248. void MessageVector::deleteLine(const U32 position)
  249. {
  250. AssertFatal(position >= 0 && position < (U32)mMessageLines.size(), "MessageVector::deleteLine: out of range position!");
  251. char* pDelete = const_cast<char*>(mMessageLines[position].message);
  252. delete [] pDelete;
  253. mMessageLines[position].message = NULL;
  254. mMessageLines[position].messageTag = 0xFFFFFFFF;
  255. mMessageLines.erase(position);
  256. // Notify of delete
  257. spectatorMessage(LineDeleted, position);
  258. }
  259. //--------------------------------------------------------------------------
  260. bool MessageVector::dump( const char* filename, const char* header )
  261. {
  262. Con::printf( "Dumping message vector %s to %s...", getName(), filename );
  263. FileObject file;
  264. if ( !file.openForWrite( filename ) )
  265. return( false );
  266. // If passed a header line, write it out first:
  267. if ( header )
  268. file.writeLine( (const U8*) header );
  269. // First write out the record count:
  270. char* lineBuf = (char*) dMalloc( 10 );
  271. dSprintf( lineBuf, 10, "%d", mMessageLines.size() );
  272. file.writeLine( (const U8*) lineBuf );
  273. // Write all of the lines of the message vector:
  274. U32 len;
  275. for ( U32 i = 0; i < (U32)mMessageLines.size(); i++ )
  276. {
  277. len = ( dStrlen( mMessageLines[i].message ) * 2 ) + 10;
  278. lineBuf = (char*) dRealloc( lineBuf, len );
  279. dSprintf( lineBuf, len, "%d ", mMessageLines[i].messageTag );
  280. expandEscape( lineBuf + dStrlen( lineBuf ), mMessageLines[i].message );
  281. file.writeLine( (const U8*) lineBuf );
  282. }
  283. file.close();
  284. return( true );
  285. }
  286. //--------------------------------------------------------------------------
  287. void MessageVector::registerSpectator(SpectatorCallback callBack, void *spectatorKey)
  288. {
  289. AssertFatal(callBack != NULL, "Error, must have a callback!");
  290. // First, make sure that this hasn't been registered already...
  291. U32 i;
  292. for (i = 0; i < (U32)mSpectators.size(); i++) {
  293. AssertFatal(mSpectators[i].key != spectatorKey, "Error, spectator key already registered!");
  294. if (mSpectators[i].key == spectatorKey)
  295. return;
  296. }
  297. mSpectators.increment();
  298. mSpectators.last().callback = callBack;
  299. mSpectators.last().key = spectatorKey;
  300. // Need to message this spectator of all the lines currently inserted...
  301. for (i = 0; i < (U32)mMessageLines.size(); i++) {
  302. (*mSpectators.last().callback)(mSpectators.last().key,
  303. LineInserted, i);
  304. }
  305. }
  306. void MessageVector::unregisterSpectator(void * spectatorKey)
  307. {
  308. for (U32 i = 0; i < (U32)mSpectators.size(); i++) {
  309. if (mSpectators[i].key == spectatorKey) {
  310. // Need to message this spectator of all the lines currently inserted...
  311. for (S32 j = mMessageLines.size() - 1; j >= 0 ; j--) {
  312. (*mSpectators[i].callback)(mSpectators[i].key,
  313. LineDeleted, j);
  314. }
  315. mSpectators.erase(i);
  316. return;
  317. }
  318. }
  319. AssertFatal(false, "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
  320. Con::errorf(ConsoleLogEntry::General,
  321. "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
  322. }
  323. void MessageVector::spectatorMessage(MessageCode code, const U32 arg)
  324. {
  325. for (U32 i = 0; i < (U32)mSpectators.size(); i++) {
  326. (*mSpectators[i].callback)(mSpectators[i].key,
  327. code, arg);
  328. }
  329. }