hbfloatertextinput.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /**
  2. * @file hbfloatertextinput.cpp
  3. * @brief HBFloaterTextInput class implementation
  4. *
  5. * $LicenseInfo:firstyear=2012&license=viewergpl$
  6. *
  7. * Copyright (c) 2012-2023, Henri Beauchamp.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "hbfloatertextinput.h"
  34. #include "lllineeditor.h"
  35. #include "lltexteditor.h"
  36. #include "lluictrlfactory.h"
  37. #include "llagent.h"
  38. #include "llchatbar.h"
  39. #include "llviewercontrol.h"
  40. //static
  41. std::map<LLLineEditor*, HBFloaterTextInput*> HBFloaterTextInput::sInstancesMap;
  42. HBFloaterTextInput::HBFloaterTextInput(LLLineEditor* input_line,
  43. const std::string& dest,
  44. void (*typing_callback)(void*, bool),
  45. void* callback_data)
  46. : LLFloater("text input"),
  47. mCallerLineEditor(input_line),
  48. mIsChatInput(dest.empty()),
  49. mTypingCallback(typing_callback),
  50. mTypingCallbackData(callback_data),
  51. mMustClose(false)
  52. {
  53. LLUICtrlFactory::getInstance()->buildFloater(this,
  54. "floater_text_input.xml");
  55. std::string title;
  56. if (mIsChatInput)
  57. {
  58. title = getString("chat");
  59. mRectControl = "ChatInputEditorRect";
  60. }
  61. else
  62. {
  63. LLStringUtil::format_map_t arg;
  64. arg["[NAME]"] = dest;
  65. title = getString("im", arg);
  66. mRectControl = "IMInputEditorRect";
  67. }
  68. setTitle(title);
  69. LLRect rect = gSavedSettings.getRect(mRectControl.c_str());
  70. reshape(rect.getWidth(), rect.getHeight());
  71. setRect(rect);
  72. sInstancesMap.emplace(input_line, this);
  73. }
  74. //virtual
  75. HBFloaterTextInput::~HBFloaterTextInput()
  76. {
  77. if (mCallerLineEditor)
  78. {
  79. std::map<LLLineEditor*, HBFloaterTextInput*>::iterator it;
  80. it = sInstancesMap.find(mCallerLineEditor);
  81. if (it != sInstancesMap.end())
  82. {
  83. sInstancesMap.erase(it);
  84. }
  85. else
  86. {
  87. llwarns << "Could not find the floater in the instances map"
  88. << llendl;
  89. }
  90. mCallerLineEditor->setText(mTextEditor->getText());
  91. mCallerLineEditor->setCursorToEnd();
  92. mCallerLineEditor->setFocus(true);
  93. if (mIsChatInput)
  94. {
  95. gAgent.stopTyping();
  96. }
  97. else if (mTypingCallback)
  98. {
  99. mTypingCallback(mTypingCallbackData, false);
  100. }
  101. }
  102. gSavedSettings.setRect(mRectControl.c_str(), getRect());
  103. }
  104. //virtual
  105. bool HBFloaterTextInput::postBuild()
  106. {
  107. mTextEditor = getChild<LLTextEditor>("text");
  108. mTextEditor->setFocusLostCallback(onTextEditorFocusLost, this);
  109. mTextEditor->setKeystrokeCallback(onTextEditorKeystroke, this);
  110. mTextEditor->setOnHandleKeyCallback(onHandleKeyCallback, this);
  111. if (mCallerLineEditor)
  112. {
  113. mTextEditor->setText(mCallerLineEditor->getText());
  114. mTextEditor->setCursorPos(mCallerLineEditor->getCursor());
  115. }
  116. mTextEditor->setFocus(true);
  117. mTextEditor->setCustomMenuType("text_input");
  118. if (mIsChatInput && gSavedSettings.getBool("TabAutoCompleteName"))
  119. {
  120. mTextEditor->setTabsToNextField(false);
  121. }
  122. return true;
  123. }
  124. //virtual
  125. void HBFloaterTextInput::draw()
  126. {
  127. if (mMustClose)
  128. {
  129. close();
  130. }
  131. else
  132. {
  133. LLFloater::draw();
  134. }
  135. }
  136. //static
  137. HBFloaterTextInput* HBFloaterTextInput::show(LLLineEditor* input_line,
  138. const std::string& dest,
  139. void (*typing_callback)(void*, bool),
  140. void* callback_data)
  141. {
  142. HBFloaterTextInput* instance = NULL;
  143. std::map<LLLineEditor*, HBFloaterTextInput*>::iterator it;
  144. it = sInstancesMap.find(input_line);
  145. if (it != sInstancesMap.end())
  146. {
  147. instance = it->second;
  148. instance->setFocus(true);
  149. instance->open();
  150. }
  151. else
  152. {
  153. instance = new HBFloaterTextInput(input_line, dest, typing_callback,
  154. callback_data);
  155. }
  156. return instance;
  157. }
  158. // This method *must* be invoked by the caller of show() when it gets
  159. // destroyed. It ensures the text input floater gets destroyed in its turn
  160. // and does not attempt to call back methods pertaining to the destroyed
  161. // caller object or its children.
  162. //static
  163. void HBFloaterTextInput::abort(LLLineEditor* input_line)
  164. {
  165. std::map<LLLineEditor*, HBFloaterTextInput*>::iterator it;
  166. it = sInstancesMap.find(input_line);
  167. if (it != sInstancesMap.end())
  168. {
  169. HBFloaterTextInput* self = it->second;
  170. self->mCallerLineEditor = NULL;
  171. self->close();
  172. sInstancesMap.erase(it);
  173. }
  174. }
  175. //static
  176. bool HBFloaterTextInput::hasFloaterFor(LLLineEditor* input_line)
  177. {
  178. return sInstancesMap.find(input_line) != sInstancesMap.end();
  179. }
  180. //static
  181. void HBFloaterTextInput::onTextEditorFocusLost(LLFocusableElement* caller,
  182. void* userdata)
  183. {
  184. HBFloaterTextInput* self = (HBFloaterTextInput*)userdata;
  185. if (self)
  186. {
  187. if (self->mIsChatInput)
  188. {
  189. gAgent.stopTyping();
  190. }
  191. else if (self->mTypingCallback)
  192. {
  193. self->mTypingCallback(self->mTypingCallbackData, false);
  194. }
  195. }
  196. }
  197. //static
  198. void HBFloaterTextInput::onTextEditorKeystroke(LLTextEditor* caller,
  199. void* userdata)
  200. {
  201. HBFloaterTextInput* self = (HBFloaterTextInput*)userdata;
  202. if (self)
  203. {
  204. if (self->mIsChatInput)
  205. {
  206. std::string text = self->mTextEditor->getText();
  207. if (text.length() > 0 && text[0] != '/')
  208. {
  209. gAgent.startTyping();
  210. }
  211. }
  212. else if (self->mTypingCallback)
  213. {
  214. self->mTypingCallback(self->mTypingCallbackData, true);
  215. }
  216. }
  217. }
  218. //static
  219. bool HBFloaterTextInput::onHandleKeyCallback(KEY key, MASK mask,
  220. LLTextEditor* caller,
  221. void* userdata)
  222. {
  223. bool handled = false;
  224. HBFloaterTextInput* self = (HBFloaterTextInput*)userdata;
  225. if (self)
  226. {
  227. if (key == KEY_RETURN)
  228. {
  229. if (mask == MASK_NONE)
  230. {
  231. // Flag for closing. We cannot close now because then we would
  232. // destroy the object to which pertains the method that called
  233. // us...
  234. self->mMustClose = true;
  235. handled = true;
  236. }
  237. else if (mask == (MASK_SHIFT | MASK_CONTROL))
  238. {
  239. S32 cursor = self->mTextEditor->getCursorPos();
  240. std::string text = self->mTextEditor->getText();
  241. // For some reason, the event is triggered twice: let's insert
  242. // only one newline character.
  243. if (cursor == 0 || text[cursor - 1] != '\n')
  244. {
  245. text = text.insert(cursor, "\n");
  246. self->mTextEditor->setText(text);
  247. self->mTextEditor->setCursorPos(cursor + 1);
  248. }
  249. handled = true;
  250. }
  251. }
  252. else if (KEY_TAB == key && mask == MASK_NONE)
  253. {
  254. std::string text = self->mTextEditor->getText();
  255. S32 word_start = 0;
  256. S32 word_len = 0;
  257. S32 cursor = self->mTextEditor->getCursorPos();
  258. S32 pos = cursor;
  259. if (pos > 0 && pos != (S32)text.length() - 1)
  260. {
  261. // Make sure the word will be found if the cursor is at its
  262. // end
  263. --pos;
  264. }
  265. if (self->mTextEditor->getWordBoundriesAt(pos, &word_start,
  266. &word_len))
  267. {
  268. std::string word = text.substr(word_start, word_len);
  269. std::string suggestion = LLChatBar::getMatchingAvatarName(word);
  270. if (suggestion != word)
  271. {
  272. text = text.replace(word_start, word_len, suggestion);
  273. self->mTextEditor->setText(text);
  274. S32 word_end = cursor + suggestion.length() -
  275. word.length();
  276. if (gSavedSettings.getBool("SelectAutoCompletedPart"))
  277. {
  278. self->mTextEditor->setSelection(cursor, word_end);
  279. }
  280. else
  281. {
  282. self->mTextEditor->setCursorPos(word_end);
  283. }
  284. }
  285. }
  286. handled = true;
  287. }
  288. }
  289. return handled;
  290. }