lltextbox.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /**
  2. * @file lltextbox.cpp
  3. * @brief A text display widget
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  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 "linden_common.h"
  33. #include "lltextbox.h"
  34. #include "lluictrlfactory.h"
  35. #include "llwindow.h"
  36. static const std::string LL_TEXT_BOX_TAG = "text";
  37. static LLRegisterWidget<LLTextBox> r26(LL_TEXT_BOX_TAG);
  38. LLTextBox::LLTextBox(const std::string& name,
  39. const LLRect& rect,
  40. const std::string& text,
  41. const LLFontGL* fontp,
  42. bool mouse_opaque)
  43. : LLUICtrl(name, rect, mouse_opaque, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
  44. mFontGL(fontp ? const_cast<LLFontGL*>(fontp)
  45. : LLFontGL::getFontSansSerifSmall())
  46. {
  47. initDefaults();
  48. setText(text);
  49. }
  50. LLTextBox::LLTextBox(const std::string& name,
  51. const std::string& text,
  52. F32 max_width,
  53. const LLFontGL* fontp,
  54. bool mouse_opaque)
  55. : LLUICtrl(name, LLRect(0, 0, 1, 1), mouse_opaque, NULL, NULL,
  56. FOLLOWS_LEFT | FOLLOWS_TOP),
  57. mFontGL(fontp ? const_cast<LLFontGL*>(fontp)
  58. : LLFontGL::getFontSansSerifSmall())
  59. {
  60. initDefaults();
  61. setWrappedText(text, max_width);
  62. reshapeToFitText();
  63. }
  64. LLTextBox::LLTextBox(const std::string& name_and_label, const LLRect& rect)
  65. : LLUICtrl(name_and_label, rect, true, NULL, NULL,
  66. FOLLOWS_LEFT | FOLLOWS_TOP),
  67. mFontGL(LLFontGL::getFontSansSerifSmall())
  68. {
  69. initDefaults();
  70. setText(name_and_label);
  71. }
  72. void LLTextBox::initDefaults()
  73. {
  74. mTextColor = LLUI::sLabelTextColor;
  75. mDisabledColor = LLUI::sLabelDisabledColor;
  76. mBackgroundColor = LLUI::sDefaultBackgroundColor;
  77. mBorderColor = LLUI::sDefaultHighlightLight;
  78. mHoverColor = LLUI::sLabelSelectedColor;
  79. mHoverActive = false;
  80. mHasHover = false;
  81. mBackgroundVisible = false;
  82. mBorderVisible = false;
  83. mFontStyle = LLFontGL::DROP_SHADOW_SOFT;
  84. mBorderDropShadowVisible = false;
  85. mUseEllipses = false;
  86. mLineSpacing = 0;
  87. mHPad = 0;
  88. mVPad = 0;
  89. mHAlign = LLFontGL::LEFT;
  90. mVAlign = LLFontGL::TOP;
  91. mClickedCallback = NULL;
  92. mCallbackUserData = NULL;
  93. setTabStop(false);
  94. }
  95. void LLTextBox::setFont(LLFontGL* fontp)
  96. {
  97. mFontGL = fontp ? fontp : LLFontGL::getFontSansSerifSmall();
  98. }
  99. bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
  100. {
  101. bool handled = false;
  102. // HACK: Only do this if there actually is a click callback, so that
  103. // overly large text boxes in the older UI won't start eating clicks.
  104. if (mClickedCallback)
  105. {
  106. handled = true;
  107. // Route future mouse messages here preemptively (release on mouse up).
  108. gFocusMgr.setMouseCapture(this);
  109. if (getSoundFlags() & MOUSE_DOWN)
  110. {
  111. make_ui_sound("UISndClick");
  112. }
  113. }
  114. return handled;
  115. }
  116. bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
  117. {
  118. bool handled = false;
  119. // We only handle the click if the click both started and ended within us
  120. // HACK: Only do this if there actually is a click callback, so that
  121. // overly large text boxes in the older UI won't start eating clicks.
  122. if (mClickedCallback && hasMouseCapture())
  123. {
  124. handled = true;
  125. // Release the mouse
  126. gFocusMgr.setMouseCapture(NULL);
  127. if (getSoundFlags() & MOUSE_UP)
  128. {
  129. make_ui_sound("UISndClickRelease");
  130. }
  131. // DO THIS AT THE VERY END to allow the text to be destroyed as a
  132. // result of being clicked. If mouseup in the widget, it's been clicked
  133. (*mClickedCallback)(mCallbackUserData);
  134. }
  135. return handled;
  136. }
  137. bool LLTextBox::handleHover(S32 x, S32 y, MASK mask)
  138. {
  139. bool handled = LLView::handleHover(x,y,mask);
  140. if (mHoverActive)
  141. {
  142. mHasHover = true; // This should be set every frame during a hover.
  143. gWindowp->setCursor(UI_CURSOR_ARROW);
  144. }
  145. return handled || mHasHover;
  146. }
  147. void LLTextBox::setText(const std::string& text)
  148. {
  149. mText.assign(text);
  150. setLineLengths();
  151. }
  152. void LLTextBox::setLineLengths()
  153. {
  154. mLineLengthList.clear();
  155. std::string::size_type cur = 0;
  156. std::string::size_type len = mText.getWString().size();
  157. while (cur < len)
  158. {
  159. std::string::size_type end = mText.getWString().find('\n', cur);
  160. std::string::size_type runLen;
  161. if (end == std::string::npos)
  162. {
  163. runLen = len - cur;
  164. cur = len;
  165. }
  166. else
  167. {
  168. runLen = end - cur;
  169. cur = end + 1; // skip the new line character
  170. }
  171. mLineLengthList.push_back((S32)runLen);
  172. }
  173. }
  174. void LLTextBox::setWrappedText(const std::string& in_text, F32 max_width)
  175. {
  176. if (max_width < 0.0)
  177. {
  178. max_width = (F32)getRect().getWidth();
  179. }
  180. LLWString wtext = utf8str_to_wstring(in_text);
  181. LLWString final_wtext;
  182. LLWString::size_type cur = 0;;
  183. LLWString::size_type len = wtext.size();
  184. while (cur < len)
  185. {
  186. LLWString::size_type end = wtext.find('\n', cur);
  187. if (end == LLWString::npos)
  188. {
  189. end = len;
  190. }
  191. LLWString::size_type runLen = end - cur;
  192. if (runLen > 0)
  193. {
  194. LLWString run(wtext, cur, runLen);
  195. LLWString::size_type useLen = mFontGL->maxDrawableChars(run.c_str(),
  196. max_width,
  197. runLen,
  198. true);
  199. final_wtext.append(wtext, cur, useLen);
  200. cur += useLen;
  201. }
  202. if (cur < len)
  203. {
  204. if (wtext[cur] == '\n')
  205. {
  206. cur += 1;
  207. }
  208. final_wtext += '\n';
  209. }
  210. }
  211. std::string final_text = wstring_to_utf8str(final_wtext);
  212. setText(final_text);
  213. }
  214. S32 LLTextBox::getTextPixelWidth()
  215. {
  216. S32 max_line_width = 0;
  217. if (mLineLengthList.size() > 0)
  218. {
  219. S32 cur_pos = 0;
  220. for (std::vector<S32>::iterator iter = mLineLengthList.begin(),
  221. end = mLineLengthList.end();
  222. iter != end; ++iter)
  223. {
  224. S32 line_length = *iter;
  225. S32 line_width = mFontGL->getWidth(mText.getWString().c_str(),
  226. cur_pos, line_length);
  227. if (line_width > max_line_width)
  228. {
  229. max_line_width = line_width;
  230. }
  231. cur_pos += line_length+1;
  232. }
  233. }
  234. else
  235. {
  236. max_line_width = mFontGL->getWidth(mText.getWString().c_str());
  237. }
  238. return max_line_width;
  239. }
  240. S32 LLTextBox::getTextPixelHeight()
  241. {
  242. S32 num_lines = mLineLengthList.size();
  243. if (num_lines < 1)
  244. {
  245. num_lines = 1;
  246. }
  247. return (S32)(num_lines * mFontGL->getLineHeight());
  248. }
  249. bool LLTextBox::setTextArg(const std::string& key,
  250. const std::string& text)
  251. {
  252. mText.setArg(key, text);
  253. setLineLengths();
  254. return true;
  255. }
  256. void LLTextBox::draw()
  257. {
  258. if (mBorderVisible)
  259. {
  260. gl_rect_2d_offset_local(getLocalRect(), 2, false);
  261. }
  262. if (mBorderDropShadowVisible)
  263. {
  264. gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
  265. LLUI::sColorDropShadow, LLUI::sDropShadowTooltip);
  266. }
  267. if (mBackgroundVisible)
  268. {
  269. LLRect r(0, getRect().getHeight(), getRect().getWidth(), 0);
  270. gl_rect_2d(r, mBackgroundColor);
  271. }
  272. S32 text_x = 0;
  273. switch (mHAlign)
  274. {
  275. case LLFontGL::LEFT:
  276. text_x = mHPad;
  277. break;
  278. case LLFontGL::HCENTER:
  279. text_x = getRect().getWidth() / 2;
  280. break;
  281. case LLFontGL::RIGHT:
  282. text_x = getRect().getWidth() - mHPad;
  283. break;
  284. }
  285. S32 text_y = getRect().getHeight() - mVPad;
  286. if (getEnabled())
  287. {
  288. if (mHasHover)
  289. {
  290. drawText(text_x, text_y, mHoverColor);
  291. }
  292. else
  293. {
  294. drawText(text_x, text_y, mTextColor);
  295. }
  296. }
  297. else
  298. {
  299. drawText(text_x, text_y, mDisabledColor);
  300. }
  301. if (sDebugRects)
  302. {
  303. drawDebugRect();
  304. }
  305. mHasHover = false; // This is reset every frame.
  306. }
  307. void LLTextBox::reshape(S32 width, S32 height, bool called_from_parent)
  308. {
  309. // reparse line lengths
  310. setLineLengths();
  311. LLView::reshape(width, height, called_from_parent);
  312. }
  313. void LLTextBox::drawText(S32 x, S32 y, const LLColor4& color)
  314. {
  315. if (mLineLengthList.empty())
  316. {
  317. mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color,
  318. mHAlign, mVAlign, mFontStyle, S32_MAX,
  319. getRect().getWidth(), NULL, false, mUseEllipses);
  320. }
  321. else
  322. {
  323. S32 cur_pos = 0;
  324. for (std::vector<S32>::iterator iter = mLineLengthList.begin(),
  325. end = mLineLengthList.end();
  326. iter != end; ++iter)
  327. {
  328. S32 line_length = *iter;
  329. mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color,
  330. mHAlign, mVAlign, mFontStyle, line_length,
  331. getRect().getWidth(), NULL, false, mUseEllipses);
  332. cur_pos += line_length + 1;
  333. y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing;
  334. }
  335. }
  336. }
  337. void LLTextBox::reshapeToFitText()
  338. {
  339. S32 width = getTextPixelWidth();
  340. S32 height = getTextPixelHeight();
  341. reshape(width + 2 * mHPad, height + 2 * mVPad);
  342. }
  343. //virtual
  344. const std::string& LLTextBox::getTag() const
  345. {
  346. return LL_TEXT_BOX_TAG;
  347. }
  348. // virtual
  349. LLXMLNodePtr LLTextBox::getXML(bool save_children) const
  350. {
  351. LLXMLNodePtr node = LLUICtrl::getXML();
  352. node->setName(LL_TEXT_BOX_TAG);
  353. // Attributes
  354. node->createChild("font", true)->setStringValue(LLFontGL::nameFromFont(mFontGL));
  355. node->createChild("halign", true)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
  356. addColorXML(node, mTextColor, "text_color", "LabelTextColor");
  357. addColorXML(node, mDisabledColor, "disabled_color", "LabelDisabledColor");
  358. addColorXML(node, mBackgroundColor, "bg_color", "DefaultBackgroundColor");
  359. addColorXML(node, mBorderColor, "border_color", "DefaultHighlightLight");
  360. // Contents
  361. node->setStringValue(mText);
  362. return node;
  363. }
  364. // static
  365. LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView* parent,
  366. LLUICtrlFactory* factory)
  367. {
  368. std::string name = "text_box";
  369. node->getAttributeString("name", name);
  370. LLFontGL* fontp = LLView::selectFont(node);
  371. std::string text = node->getTextContents();
  372. LLTextBox* text_box = new LLTextBox(name, LLRect(), text, fontp, false);
  373. LLFontGL::HAlign halign = LLView::selectFontHAlign(node);
  374. text_box->setHAlign(halign);
  375. text_box->initFromXML(node, parent);
  376. node->getAttributeS32("line_spacing", text_box->mLineSpacing);
  377. std::string font_style;
  378. if (node->getAttributeString("font-style", font_style))
  379. {
  380. text_box->mFontStyle = LLFontGL::getStyleFromString(font_style);
  381. }
  382. bool mouse_opaque = text_box->getMouseOpaque();
  383. if (node->getAttributeBool("mouse_opaque", mouse_opaque))
  384. {
  385. text_box->setMouseOpaque(mouse_opaque);
  386. }
  387. if (node->hasAttribute("text_color"))
  388. {
  389. LLColor4 color;
  390. LLUICtrlFactory::getAttributeColor(node, "text_color", color);
  391. text_box->setColor(color);
  392. }
  393. if (node->hasAttribute("hover_color"))
  394. {
  395. LLColor4 color;
  396. LLUICtrlFactory::getAttributeColor(node, "hover_color", color);
  397. text_box->setHoverColor(color);
  398. text_box->setHoverActive(true);
  399. }
  400. bool hover_active = false;
  401. if (node->getAttributeBool("hover", hover_active))
  402. {
  403. text_box->setHoverActive(hover_active);
  404. }
  405. return text_box;
  406. }