/indra/newview/llchathistory.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 967 lines · 744 code · 144 blank · 79 comment · 167 complexity · 35946e35f7bd1f6b345d05172c530fb9 MD5 · raw file

  1. /**
  2. * @file llchathistory.cpp
  3. * @brief LLTextEditor base class
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llchathistory.h"
  28. #include "llavatarnamecache.h"
  29. #include "llinstantmessage.h"
  30. #include "llimview.h"
  31. #include "llcommandhandler.h"
  32. #include "llpanel.h"
  33. #include "lluictrlfactory.h"
  34. #include "llscrollcontainer.h"
  35. #include "llavatariconctrl.h"
  36. #include "llcallingcard.h" //for LLAvatarTracker
  37. #include "llagentdata.h"
  38. #include "llavataractions.h"
  39. #include "lltrans.h"
  40. #include "llfloaterreg.h"
  41. #include "llfloatersidepanelcontainer.h"
  42. #include "llmutelist.h"
  43. #include "llstylemap.h"
  44. #include "llslurl.h"
  45. #include "lllayoutstack.h"
  46. #include "llagent.h"
  47. #include "llnotificationsutil.h"
  48. #include "lltoastnotifypanel.h"
  49. #include "lltooltip.h"
  50. #include "llviewerregion.h"
  51. #include "llviewertexteditor.h"
  52. #include "llworld.h"
  53. #include "lluiconstants.h"
  54. #include "llstring.h"
  55. #include "llviewercontrol.h"
  56. static LLDefaultChildRegistry::Register<LLChatHistory> r("chat_history");
  57. const static std::string NEW_LINE(rawstr_to_utf8("\n"));
  58. const static std::string SLURL_APP_AGENT = "secondlife:///app/agent/";
  59. const static std::string SLURL_ABOUT = "/about";
  60. // support for secondlife:///app/objectim/{UUID}/ SLapps
  61. class LLObjectIMHandler : public LLCommandHandler
  62. {
  63. public:
  64. // requests will be throttled from a non-trusted browser
  65. LLObjectIMHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) {}
  66. bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
  67. {
  68. if (params.size() < 1)
  69. {
  70. return false;
  71. }
  72. LLUUID object_id;
  73. if (!object_id.set(params[0], FALSE))
  74. {
  75. return false;
  76. }
  77. LLSD payload;
  78. payload["object_id"] = object_id;
  79. payload["owner_id"] = query_map["owner"];
  80. payload["name"] = query_map["name"];
  81. payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]);
  82. payload["group_owned"] = query_map["groupowned"];
  83. LLFloaterReg::showInstance("inspect_remote_object", payload);
  84. return true;
  85. }
  86. };
  87. LLObjectIMHandler gObjectIMHandler;
  88. class LLChatHistoryHeader: public LLPanel
  89. {
  90. public:
  91. LLChatHistoryHeader()
  92. : LLPanel(),
  93. mPopupMenuHandleAvatar(),
  94. mPopupMenuHandleObject(),
  95. mAvatarID(),
  96. mSourceType(CHAT_SOURCE_UNKNOWN),
  97. mFrom(),
  98. mSessionID(),
  99. mMinUserNameWidth(0),
  100. mUserNameFont(NULL)
  101. {}
  102. static LLChatHistoryHeader* createInstance(const std::string& file_name)
  103. {
  104. LLChatHistoryHeader* pInstance = new LLChatHistoryHeader;
  105. pInstance->buildFromFile(file_name);
  106. return pInstance;
  107. }
  108. ~LLChatHistoryHeader()
  109. {
  110. // Detach the info button so that it doesn't get destroyed (EXT-8463).
  111. hideInfoCtrl();
  112. }
  113. BOOL handleMouseUp(S32 x, S32 y, MASK mask)
  114. {
  115. return LLPanel::handleMouseUp(x,y,mask);
  116. }
  117. void onObjectIconContextMenuItemClicked(const LLSD& userdata)
  118. {
  119. std::string level = userdata.asString();
  120. if (level == "profile")
  121. {
  122. LLFloaterReg::showInstance("inspect_remote_object", mObjectData);
  123. }
  124. else if (level == "block")
  125. {
  126. LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT));
  127. LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with("blocked_to_select", getAvatarId()));
  128. }
  129. }
  130. void onAvatarIconContextMenuItemClicked(const LLSD& userdata)
  131. {
  132. std::string level = userdata.asString();
  133. if (level == "profile")
  134. {
  135. LLAvatarActions::showProfile(getAvatarId());
  136. }
  137. else if (level == "im")
  138. {
  139. LLAvatarActions::startIM(getAvatarId());
  140. }
  141. else if (level == "add")
  142. {
  143. LLAvatarActions::requestFriendshipDialog(getAvatarId(), mFrom);
  144. }
  145. else if (level == "remove")
  146. {
  147. LLAvatarActions::removeFriendDialog(getAvatarId());
  148. }
  149. }
  150. BOOL postBuild()
  151. {
  152. LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
  153. registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2));
  154. registrar.add("ObjectIcon.Action", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemClicked, this, _2));
  155. LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
  156. mPopupMenuHandleAvatar = menu->getHandle();
  157. menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_object_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
  158. mPopupMenuHandleObject = menu->getHandle();
  159. setDoubleClickCallback(boost::bind(&LLChatHistoryHeader::showInspector, this));
  160. setMouseEnterCallback(boost::bind(&LLChatHistoryHeader::showInfoCtrl, this));
  161. setMouseLeaveCallback(boost::bind(&LLChatHistoryHeader::hideInfoCtrl, this));
  162. return LLPanel::postBuild();
  163. }
  164. bool pointInChild(const std::string& name,S32 x,S32 y)
  165. {
  166. LLUICtrl* child = findChild<LLUICtrl>(name);
  167. if(!child)
  168. return false;
  169. LLView* parent = child->getParent();
  170. if(parent!=this)
  171. {
  172. x-=parent->getRect().mLeft;
  173. y-=parent->getRect().mBottom;
  174. }
  175. S32 local_x = x - child->getRect().mLeft ;
  176. S32 local_y = y - child->getRect().mBottom ;
  177. return child->pointInView(local_x, local_y);
  178. }
  179. BOOL handleRightMouseDown(S32 x, S32 y, MASK mask)
  180. {
  181. if(pointInChild("avatar_icon",x,y) || pointInChild("user_name",x,y))
  182. {
  183. showContextMenu(x,y);
  184. return TRUE;
  185. }
  186. return LLPanel::handleRightMouseDown(x,y,mask);
  187. }
  188. void showInspector()
  189. {
  190. if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType) return;
  191. if (mSourceType == CHAT_SOURCE_OBJECT)
  192. {
  193. LLFloaterReg::showInstance("inspect_remote_object", mObjectData);
  194. }
  195. else if (mSourceType == CHAT_SOURCE_AGENT)
  196. {
  197. LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarID));
  198. }
  199. //if chat source is system, you may add "else" here to define behaviour.
  200. }
  201. static void onClickInfoCtrl(LLUICtrl* info_ctrl)
  202. {
  203. if (!info_ctrl) return;
  204. LLChatHistoryHeader* header = dynamic_cast<LLChatHistoryHeader*>(info_ctrl->getParent());
  205. if (!header) return;
  206. header->showInspector();
  207. }
  208. const LLUUID& getAvatarId () const { return mAvatarID;}
  209. void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args)
  210. {
  211. mAvatarID = chat.mFromID;
  212. mSessionID = chat.mSessionID;
  213. mSourceType = chat.mSourceType;
  214. //*TODO overly defensive thing, source type should be maintained out there
  215. if((chat.mFromID.isNull() && chat.mFromName.empty()) || (chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull()))
  216. {
  217. mSourceType = CHAT_SOURCE_SYSTEM;
  218. }
  219. mUserNameFont = style_params.font();
  220. LLTextBox* user_name = getChild<LLTextBox>("user_name");
  221. user_name->setReadOnlyColor(style_params.readonly_color());
  222. user_name->setColor(style_params.color());
  223. if (chat.mFromName.empty()
  224. || mSourceType == CHAT_SOURCE_SYSTEM)
  225. {
  226. mFrom = LLTrans::getString("SECOND_LIFE");
  227. user_name->setValue(mFrom);
  228. updateMinUserNameWidth();
  229. }
  230. else if (mSourceType == CHAT_SOURCE_AGENT
  231. && !mAvatarID.isNull()
  232. && chat.mChatStyle != CHAT_STYLE_HISTORY)
  233. {
  234. // ...from a normal user, lookup the name and fill in later.
  235. // *NOTE: Do not do this for chat history logs, otherwise the viewer
  236. // will flood the People API with lookup requests on startup
  237. // Start with blank so sample data from XUI XML doesn't
  238. // flash on the screen
  239. user_name->setValue( LLSD() );
  240. LLAvatarNameCache::get(mAvatarID,
  241. boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2));
  242. }
  243. else if (chat.mChatStyle == CHAT_STYLE_HISTORY ||
  244. mSourceType == CHAT_SOURCE_AGENT)
  245. {
  246. //if it's an avatar name with a username add formatting
  247. S32 username_start = chat.mFromName.rfind(" (");
  248. S32 username_end = chat.mFromName.rfind(')');
  249. if (username_start != std::string::npos &&
  250. username_end == (chat.mFromName.length() - 1))
  251. {
  252. mFrom = chat.mFromName.substr(0, username_start);
  253. user_name->setValue(mFrom);
  254. if (gSavedSettings.getBOOL("NameTagShowUsernames"))
  255. {
  256. std::string username = chat.mFromName.substr(username_start + 2);
  257. username = username.substr(0, username.length() - 1);
  258. LLStyle::Params style_params_name;
  259. LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor");
  260. style_params_name.color(userNameColor);
  261. style_params_name.font.name("SansSerifSmall");
  262. style_params_name.font.style("NORMAL");
  263. style_params_name.readonly_color(userNameColor);
  264. user_name->appendText(" - " + username, FALSE, style_params_name);
  265. }
  266. }
  267. else
  268. {
  269. mFrom = chat.mFromName;
  270. user_name->setValue(mFrom);
  271. updateMinUserNameWidth();
  272. }
  273. }
  274. else
  275. {
  276. // ...from an object, just use name as given
  277. mFrom = chat.mFromName;
  278. user_name->setValue(mFrom);
  279. updateMinUserNameWidth();
  280. }
  281. setTimeField(chat);
  282. // Set up the icon.
  283. LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon");
  284. if(mSourceType != CHAT_SOURCE_AGENT || mAvatarID.isNull())
  285. icon->setDrawTooltip(false);
  286. switch (mSourceType)
  287. {
  288. case CHAT_SOURCE_AGENT:
  289. icon->setValue(chat.mFromID);
  290. break;
  291. case CHAT_SOURCE_OBJECT:
  292. icon->setValue(LLSD("OBJECT_Icon"));
  293. break;
  294. case CHAT_SOURCE_SYSTEM:
  295. icon->setValue(LLSD("SL_Logo"));
  296. break;
  297. case CHAT_SOURCE_UNKNOWN:
  298. icon->setValue(LLSD("Unknown_Icon"));
  299. }
  300. // In case the message came from an object, save the object info
  301. // to be able properly show its profile.
  302. if ( chat.mSourceType == CHAT_SOURCE_OBJECT)
  303. {
  304. std::string slurl = args["slurl"].asString();
  305. if (slurl.empty())
  306. {
  307. LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent);
  308. if(region)
  309. {
  310. LLSLURL region_slurl(region->getName(), chat.mPosAgent);
  311. slurl = region_slurl.getLocationString();
  312. }
  313. }
  314. LLSD payload;
  315. payload["object_id"] = chat.mFromID;
  316. payload["name"] = chat.mFromName;
  317. payload["owner_id"] = chat.mOwnerID;
  318. payload["slurl"] = LLWeb::escapeURL(slurl);
  319. mObjectData = payload;
  320. }
  321. }
  322. /*virtual*/ void draw()
  323. {
  324. LLTextBox* user_name = getChild<LLTextBox>("user_name");
  325. LLTextBox* time_box = getChild<LLTextBox>("time_box");
  326. LLRect user_name_rect = user_name->getRect();
  327. S32 user_name_width = user_name_rect.getWidth();
  328. S32 time_box_width = time_box->getRect().getWidth();
  329. if (time_box->getVisible() && user_name_width <= mMinUserNameWidth)
  330. {
  331. time_box->setVisible(FALSE);
  332. user_name_rect.mRight += time_box_width;
  333. user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight());
  334. user_name->setRect(user_name_rect);
  335. }
  336. if (!time_box->getVisible() && user_name_width > mMinUserNameWidth + time_box_width)
  337. {
  338. user_name_rect.mRight -= time_box_width;
  339. user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight());
  340. user_name->setRect(user_name_rect);
  341. time_box->setVisible(TRUE);
  342. }
  343. LLPanel::draw();
  344. }
  345. void updateMinUserNameWidth()
  346. {
  347. if (mUserNameFont)
  348. {
  349. LLTextBox* user_name = getChild<LLTextBox>("user_name");
  350. const LLWString& text = user_name->getWText();
  351. mMinUserNameWidth = mUserNameFont->getWidth(text.c_str()) + PADDING;
  352. }
  353. }
  354. void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
  355. {
  356. mFrom = av_name.mDisplayName;
  357. LLTextBox* user_name = getChild<LLTextBox>("user_name");
  358. user_name->setValue( LLSD(av_name.mDisplayName ) );
  359. user_name->setToolTip( av_name.mUsername );
  360. if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
  361. LLAvatarNameCache::useDisplayNames() &&
  362. !av_name.mIsDisplayNameDefault)
  363. {
  364. LLStyle::Params style_params_name;
  365. LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor");
  366. style_params_name.color(userNameColor);
  367. style_params_name.font.name("SansSerifSmall");
  368. style_params_name.font.style("NORMAL");
  369. style_params_name.readonly_color(userNameColor);
  370. user_name->appendText(" - " + av_name.mUsername, FALSE, style_params_name);
  371. }
  372. setToolTip( av_name.mUsername );
  373. // name might have changed, update width
  374. updateMinUserNameWidth();
  375. }
  376. protected:
  377. static const S32 PADDING = 20;
  378. void showContextMenu(S32 x,S32 y)
  379. {
  380. if(mSourceType == CHAT_SOURCE_SYSTEM)
  381. showSystemContextMenu(x,y);
  382. if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_AGENT)
  383. showAvatarContextMenu(x,y);
  384. if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_OBJECT && SYSTEM_FROM != mFrom)
  385. showObjectContextMenu(x,y);
  386. }
  387. void showSystemContextMenu(S32 x,S32 y)
  388. {
  389. }
  390. void showObjectContextMenu(S32 x,S32 y)
  391. {
  392. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleObject.get();
  393. if(menu)
  394. LLMenuGL::showPopup(this, menu, x, y);
  395. }
  396. void showAvatarContextMenu(S32 x,S32 y)
  397. {
  398. LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleAvatar.get();
  399. if(menu)
  400. {
  401. bool is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL;
  402. menu->setItemEnabled("Add Friend", !is_friend);
  403. menu->setItemEnabled("Remove Friend", is_friend);
  404. if(gAgentID == mAvatarID)
  405. {
  406. menu->setItemEnabled("Add Friend", false);
  407. menu->setItemEnabled("Send IM", false);
  408. menu->setItemEnabled("Remove Friend", false);
  409. }
  410. if (mSessionID == LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, mAvatarID))
  411. {
  412. menu->setItemVisible("Send IM", false);
  413. }
  414. menu->buildDrawLabels();
  415. menu->updateParent(LLMenuGL::sMenuContainer);
  416. LLMenuGL::showPopup(this, menu, x, y);
  417. }
  418. }
  419. void showInfoCtrl()
  420. {
  421. if (mAvatarID.isNull() || mFrom.empty() || SYSTEM_FROM == mFrom) return;
  422. if (!sInfoCtrl)
  423. {
  424. // *TODO: Delete the button at exit.
  425. sInfoCtrl = LLUICtrlFactory::createFromFile<LLUICtrl>("inspector_info_ctrl.xml", NULL, LLPanel::child_registry_t::instance());
  426. if (sInfoCtrl)
  427. {
  428. sInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, sInfoCtrl));
  429. }
  430. }
  431. if (!sInfoCtrl)
  432. {
  433. llassert(sInfoCtrl != NULL);
  434. return;
  435. }
  436. LLTextBox* name = getChild<LLTextBox>("user_name");
  437. LLRect sticky_rect = name->getRect();
  438. S32 icon_x = llmin(sticky_rect.mLeft + name->getTextBoundingRect().getWidth() + 7, sticky_rect.mRight - 3);
  439. sInfoCtrl->setOrigin(icon_x, sticky_rect.getCenterY() - sInfoCtrl->getRect().getHeight() / 2 ) ;
  440. addChild(sInfoCtrl);
  441. }
  442. void hideInfoCtrl()
  443. {
  444. if (!sInfoCtrl) return;
  445. if (sInfoCtrl->getParent() == this)
  446. {
  447. removeChild(sInfoCtrl);
  448. }
  449. }
  450. private:
  451. void setTimeField(const LLChat& chat)
  452. {
  453. LLTextBox* time_box = getChild<LLTextBox>("time_box");
  454. LLRect rect_before = time_box->getRect();
  455. time_box->setValue(chat.mTimeStr);
  456. // set necessary textbox width to fit all text
  457. time_box->reshapeToFitText();
  458. LLRect rect_after = time_box->getRect();
  459. // move rect to the left to correct position...
  460. S32 delta_pos_x = rect_before.getWidth() - rect_after.getWidth();
  461. S32 delta_pos_y = rect_before.getHeight() - rect_after.getHeight();
  462. time_box->translate(delta_pos_x, delta_pos_y);
  463. //... & change width of the name control
  464. LLView* user_name = getChild<LLView>("user_name");
  465. const LLRect& user_rect = user_name->getRect();
  466. user_name->reshape(user_rect.getWidth() + delta_pos_x, user_rect.getHeight());
  467. }
  468. protected:
  469. LLHandle<LLView> mPopupMenuHandleAvatar;
  470. LLHandle<LLView> mPopupMenuHandleObject;
  471. static LLUICtrl* sInfoCtrl;
  472. LLUUID mAvatarID;
  473. LLSD mObjectData;
  474. EChatSourceType mSourceType;
  475. std::string mFrom;
  476. LLUUID mSessionID;
  477. S32 mMinUserNameWidth;
  478. const LLFontGL* mUserNameFont;
  479. };
  480. LLUICtrl* LLChatHistoryHeader::sInfoCtrl = NULL;
  481. LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
  482. : LLUICtrl(p),
  483. mMessageHeaderFilename(p.message_header),
  484. mMessageSeparatorFilename(p.message_separator),
  485. mLeftTextPad(p.left_text_pad),
  486. mRightTextPad(p.right_text_pad),
  487. mLeftWidgetPad(p.left_widget_pad),
  488. mRightWidgetPad(p.right_widget_pad),
  489. mTopSeparatorPad(p.top_separator_pad),
  490. mBottomSeparatorPad(p.bottom_separator_pad),
  491. mTopHeaderPad(p.top_header_pad),
  492. mBottomHeaderPad(p.bottom_header_pad),
  493. mIsLastMessageFromLog(false)
  494. {
  495. LLTextEditor::Params editor_params(p);
  496. editor_params.rect = getLocalRect();
  497. editor_params.follows.flags = FOLLOWS_ALL;
  498. editor_params.enabled = false; // read only
  499. editor_params.show_context_menu = "true";
  500. mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
  501. }
  502. LLChatHistory::~LLChatHistory()
  503. {
  504. this->clear();
  505. }
  506. void LLChatHistory::initFromParams(const LLChatHistory::Params& p)
  507. {
  508. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  509. LLRect stack_rect = getLocalRect();
  510. stack_rect.mRight -= scrollbar_size;
  511. LLLayoutStack::Params layout_p;
  512. layout_p.rect = stack_rect;
  513. layout_p.follows.flags = FOLLOWS_ALL;
  514. layout_p.orientation = LLLayoutStack::VERTICAL;
  515. layout_p.mouse_opaque = false;
  516. LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p, this);
  517. const S32 NEW_TEXT_NOTICE_HEIGHT = 20;
  518. LLLayoutPanel::Params panel_p;
  519. panel_p.name = "spacer";
  520. panel_p.background_visible = false;
  521. panel_p.has_border = false;
  522. panel_p.mouse_opaque = false;
  523. panel_p.min_dim = 30;
  524. panel_p.auto_resize = true;
  525. panel_p.user_resize = false;
  526. stackp->addPanel(LLUICtrlFactory::create<LLLayoutPanel>(panel_p), LLLayoutStack::ANIMATE);
  527. panel_p.name = "new_text_notice_holder";
  528. LLRect new_text_notice_rect = getLocalRect();
  529. new_text_notice_rect.mTop = new_text_notice_rect.mBottom + NEW_TEXT_NOTICE_HEIGHT;
  530. panel_p.rect = new_text_notice_rect;
  531. panel_p.background_opaque = true;
  532. panel_p.background_visible = true;
  533. panel_p.visible = false;
  534. panel_p.min_dim = 0;
  535. panel_p.auto_resize = false;
  536. panel_p.user_resize = false;
  537. mMoreChatPanel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
  538. LLTextBox::Params text_p(p.more_chat_text);
  539. text_p.rect = mMoreChatPanel->getLocalRect();
  540. text_p.follows.flags = FOLLOWS_ALL;
  541. text_p.name = "more_chat_text";
  542. mMoreChatText = LLUICtrlFactory::create<LLTextBox>(text_p, mMoreChatPanel);
  543. mMoreChatText->setClickedCallback(boost::bind(&LLChatHistory::onClickMoreText, this));
  544. stackp->addPanel(mMoreChatPanel, LLLayoutStack::ANIMATE);
  545. }
  546. /*void LLChatHistory::updateTextRect()
  547. {
  548. static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0);
  549. LLRect old_text_rect = mVisibleTextRect;
  550. mVisibleTextRect = mScroller->getContentWindowRect();
  551. mVisibleTextRect.stretch(-texteditor_border);
  552. mVisibleTextRect.mLeft += mLeftTextPad;
  553. mVisibleTextRect.mRight -= mRightTextPad;
  554. if (mVisibleTextRect != old_text_rect)
  555. {
  556. needsReflow();
  557. }
  558. }*/
  559. LLView* LLChatHistory::getSeparator()
  560. {
  561. LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance());
  562. return separator;
  563. }
  564. LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args)
  565. {
  566. LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename);
  567. header->setup(chat, style_params, args);
  568. return header;
  569. }
  570. void LLChatHistory::onClickMoreText()
  571. {
  572. mEditor->endOfDoc();
  573. }
  574. void LLChatHistory::clear()
  575. {
  576. mLastFromName.clear();
  577. mEditor->clear();
  578. mLastFromID = LLUUID::null;
  579. }
  580. void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LLStyle::Params& input_append_params)
  581. {
  582. bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean();
  583. llassert(mEditor);
  584. if (!mEditor)
  585. {
  586. return;
  587. }
  588. mEditor->setPlainText(use_plain_text_chat_history);
  589. if (!mEditor->scrolledToEnd() && chat.mFromID != gAgent.getID() && !chat.mFromName.empty())
  590. {
  591. mUnreadChatSources.insert(chat.mFromName);
  592. mMoreChatPanel->setVisible(TRUE);
  593. std::string chatters;
  594. for (unread_chat_source_t::iterator it = mUnreadChatSources.begin();
  595. it != mUnreadChatSources.end();)
  596. {
  597. chatters += *it;
  598. if (++it != mUnreadChatSources.end())
  599. {
  600. chatters += ", ";
  601. }
  602. }
  603. LLStringUtil::format_map_t args;
  604. args["SOURCES"] = chatters;
  605. if (mUnreadChatSources.size() == 1)
  606. {
  607. mMoreChatText->setValue(LLTrans::getString("unread_chat_single", args));
  608. }
  609. else
  610. {
  611. mMoreChatText->setValue(LLTrans::getString("unread_chat_multiple", args));
  612. }
  613. S32 height = mMoreChatText->getTextPixelHeight() + 5;
  614. mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height);
  615. }
  616. LLColor4 txt_color = LLUIColorTable::instance().getColor("White");
  617. LLViewerChat::getChatColor(chat,txt_color);
  618. LLFontGL* fontp = LLViewerChat::getChatFont();
  619. std::string font_name = LLFontGL::nameFromFont(fontp);
  620. std::string font_size = LLFontGL::sizeFromFont(fontp);
  621. LLStyle::Params style_params;
  622. style_params.color(txt_color);
  623. style_params.readonly_color(txt_color);
  624. style_params.font.name(font_name);
  625. style_params.font.size(font_size);
  626. style_params.font.style(input_append_params.font.style);
  627. std::string prefix = chat.mText.substr(0, 4);
  628. //IRC styled /me messages.
  629. bool irc_me = prefix == "/me " || prefix == "/me'";
  630. // Delimiter after a name in header copy/past and in plain text mode
  631. std::string delimiter = ": ";
  632. std::string shout = LLTrans::getString("shout");
  633. std::string whisper = LLTrans::getString("whisper");
  634. if (chat.mChatType == CHAT_TYPE_SHOUT ||
  635. chat.mChatType == CHAT_TYPE_WHISPER ||
  636. chat.mText.compare(0, shout.length(), shout) == 0 ||
  637. chat.mText.compare(0, whisper.length(), whisper) == 0)
  638. {
  639. delimiter = " ";
  640. }
  641. // Don't add any delimiter after name in irc styled messages
  642. if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC)
  643. {
  644. delimiter = LLStringUtil::null;
  645. style_params.font.style = "ITALIC";
  646. }
  647. bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY;
  648. // We graying out chat history by graying out messages that contains full date in a time string
  649. if (message_from_log)
  650. {
  651. style_params.color(LLColor4::grey);
  652. style_params.readonly_color(LLColor4::grey);
  653. }
  654. if (use_plain_text_chat_history)
  655. {
  656. LLStyle::Params timestamp_style(style_params);
  657. if (!message_from_log)
  658. {
  659. LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor");
  660. timestamp_style.color(timestamp_color);
  661. timestamp_style.readonly_color(timestamp_color);
  662. }
  663. mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getText().size() != 0, timestamp_style);
  664. if (utf8str_trim(chat.mFromName).size() != 0)
  665. {
  666. // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
  667. if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())
  668. {
  669. // for object IMs, create a secondlife:///app/objectim SLapp
  670. std::string url = LLViewerChat::getSenderSLURL(chat, args);
  671. // set the link for the object name to be the objectim SLapp
  672. // (don't let object names with hyperlinks override our objectim Url)
  673. LLStyle::Params link_params(style_params);
  674. LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  675. link_params.color = link_color;
  676. link_params.readonly_color = link_color;
  677. link_params.is_link = true;
  678. link_params.link_href = url;
  679. mEditor->appendText(chat.mFromName + delimiter,
  680. false, link_params);
  681. }
  682. else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log)
  683. {
  684. LLStyle::Params link_params(style_params);
  685. link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID));
  686. // Add link to avatar's inspector and delimiter to message.
  687. mEditor->appendText(std::string(link_params.link_href) + delimiter, false, link_params);
  688. }
  689. else
  690. {
  691. mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, false, style_params);
  692. }
  693. }
  694. }
  695. else
  696. {
  697. LLView* view = NULL;
  698. LLInlineViewSegment::Params p;
  699. p.force_newline = true;
  700. p.left_pad = mLeftWidgetPad;
  701. p.right_pad = mRightWidgetPad;
  702. LLDate new_message_time = LLDate::now();
  703. if (mLastFromName == chat.mFromName
  704. && mLastFromID == chat.mFromID
  705. && mLastMessageTime.notNull()
  706. && (new_message_time.secondsSinceEpoch() - mLastMessageTime.secondsSinceEpoch()) < 60.0
  707. && mIsLastMessageFromLog == message_from_log) //distinguish between current and previous chat session's histories
  708. {
  709. view = getSeparator();
  710. p.top_pad = mTopSeparatorPad;
  711. p.bottom_pad = mBottomSeparatorPad;
  712. }
  713. else
  714. {
  715. view = getHeader(chat, style_params, args);
  716. if (mEditor->getText().size() == 0)
  717. p.top_pad = 0;
  718. else
  719. p.top_pad = mTopHeaderPad;
  720. p.bottom_pad = mBottomHeaderPad;
  721. }
  722. p.view = view;
  723. //Prepare the rect for the view
  724. LLRect target_rect = mEditor->getDocumentView()->getRect();
  725. // squeeze down the widget by subtracting padding off left and right
  726. target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad();
  727. target_rect.mRight -= mRightWidgetPad;
  728. view->reshape(target_rect.getWidth(), view->getRect().getHeight());
  729. view->setOrigin(target_rect.mLeft, view->getRect().mBottom);
  730. std::string widget_associated_text = "\n[" + chat.mTimeStr + "] ";
  731. if (utf8str_trim(chat.mFromName).size() != 0 && chat.mFromName != SYSTEM_FROM)
  732. widget_associated_text += chat.mFromName + delimiter;
  733. mEditor->appendWidget(p, widget_associated_text, false);
  734. mLastFromName = chat.mFromName;
  735. mLastFromID = chat.mFromID;
  736. mLastMessageTime = new_message_time;
  737. mIsLastMessageFromLog = message_from_log;
  738. }
  739. if (chat.mNotifId.notNull())
  740. {
  741. LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId);
  742. if (notification != NULL)
  743. {
  744. LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel(
  745. notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history);
  746. //we can't set follows in xml since it broke toasts behavior
  747. notify_box->setFollowsLeft();
  748. notify_box->setFollowsRight();
  749. notify_box->setFollowsTop();
  750. ctrl_list_t ctrls = notify_box->getControlPanel()->getCtrlList();
  751. S32 offset = 0;
  752. // Children were added by addChild() which uses push_front to insert them into list,
  753. // so to get buttons in correct order reverse iterator is used (EXT-5906)
  754. for (ctrl_list_t::reverse_iterator it = ctrls.rbegin(); it != ctrls.rend(); it++)
  755. {
  756. LLButton * button = dynamic_cast<LLButton*> (*it);
  757. if (button != NULL)
  758. {
  759. button->setOrigin( offset,
  760. button->getRect().mBottom);
  761. button->setLeftHPad(2 * HPAD);
  762. button->setRightHPad(2 * HPAD);
  763. // set zero width before perform autoResize()
  764. button->setRect(LLRect(button->getRect().mLeft,
  765. button->getRect().mTop, button->getRect().mLeft,
  766. button->getRect().mBottom));
  767. button->setAutoResize(true);
  768. button->autoResize();
  769. offset += HPAD + button->getRect().getWidth();
  770. button->setFollowsNone();
  771. }
  772. }
  773. //Prepare the rect for the view
  774. LLRect target_rect = mEditor->getDocumentView()->getRect();
  775. // squeeze down the widget by subtracting padding off left and right
  776. target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad();
  777. target_rect.mRight -= mRightWidgetPad;
  778. notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight());
  779. notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom);
  780. LLInlineViewSegment::Params params;
  781. params.view = notify_box;
  782. params.left_pad = mLeftWidgetPad;
  783. params.right_pad = mRightWidgetPad;
  784. mEditor->appendWidget(params, "\n", false);
  785. }
  786. }
  787. else
  788. {
  789. std::string message = irc_me ? chat.mText.substr(3) : chat.mText;
  790. //MESSAGE TEXT PROCESSING
  791. //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010)
  792. if (use_plain_text_chat_history && gAgentID != chat.mFromID && chat.mFromID.notNull())
  793. {
  794. std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT;
  795. if (message.length() > slurl_about.length() &&
  796. message.compare(0, slurl_about.length(), slurl_about) == 0)
  797. {
  798. message = message.substr(slurl_about.length(), message.length()-1);
  799. }
  800. }
  801. if (irc_me && !use_plain_text_chat_history)
  802. {
  803. message = chat.mFromName + message;
  804. }
  805. mEditor->appendText(message, FALSE, style_params);
  806. }
  807. mEditor->blockUndo();
  808. // automatically scroll to end when receiving chat from myself
  809. if (chat.mFromID == gAgentID)
  810. {
  811. mEditor->setCursorAndScrollToEnd();
  812. }
  813. }
  814. void LLChatHistory::draw()
  815. {
  816. if (mEditor->scrolledToEnd())
  817. {
  818. mUnreadChatSources.clear();
  819. mMoreChatPanel->setVisible(FALSE);
  820. }
  821. LLUICtrl::draw();
  822. }