llgroupnotify.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /**
  2. * @file llgroupnotify.cpp
  3. * @brief Non-blocking notification that doesn't take keyboard focus.
  4. *
  5. * $LicenseInfo:firstyear=2006&license=viewergpl$
  6. *
  7. * Copyright (c) 2006-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 "llviewerprecompiledheaders.h"
  33. #include "llgroupnotify.h"
  34. #include "llbutton.h"
  35. #include "lliconctrl.h"
  36. #include "lltextbox.h"
  37. #include "llagent.h"
  38. #include "llfloatergroupinfo.h"
  39. #include "llinventory.h"
  40. #include "llinventoryicon.h" // For getIcon()
  41. #include "llnotify.h"
  42. #include "llviewercontrol.h"
  43. #include "llviewermessage.h" // For LLOfferInfo and formatted_time()
  44. #include "llviewertexteditor.h"
  45. constexpr F32 ANIMATION_TIME = 0.333f;
  46. S32 LLGroupNotifyBox::sGroupNotifyBoxCount = 0;
  47. bool is_openable(LLAssetType::EType type)
  48. {
  49. switch (type)
  50. {
  51. case LLAssetType::AT_LANDMARK:
  52. case LLAssetType::AT_NOTECARD:
  53. case LLAssetType::AT_IMAGE_JPEG:
  54. case LLAssetType::AT_IMAGE_TGA:
  55. case LLAssetType::AT_TEXTURE:
  56. case LLAssetType::AT_TEXTURE_TGA:
  57. return gSavedSettings.getBool("ShowNewInventory");
  58. default:
  59. return false;
  60. }
  61. }
  62. LLGroupNotifyBox::LLGroupNotifyBox(const std::string& subject,
  63. const std::string& message,
  64. const std::string& from_name,
  65. const LLUUID& group_id,
  66. const LLUUID& group_insignia,
  67. const std::string& group_name,
  68. const LLDate& time_stamp,
  69. bool has_inventory,
  70. const std::string& inventory_name,
  71. const LLSD& inventory_offer)
  72. : LLPanel("groupnotify", LLGroupNotifyBox::getGroupNotifyRect(), BORDER_YES),
  73. mAnimating(true),
  74. mGroupID(group_id),
  75. mHasInventory(has_inventory),
  76. mInventoryOffer(NULL)
  77. {
  78. constexpr S32 LABEL_WIDTH = 64;
  79. constexpr S32 ICON_WIDTH = 64;
  80. constexpr S32 VPAD = 2;
  81. constexpr S32 BOTTOM_PAD = VPAD * 2;
  82. constexpr S32 LINE_HEIGHT = 16;
  83. const S32 top = getRect().getHeight() - 32; // Get past the top menu bar
  84. const S32 right = getRect().getWidth() - HPAD - HPAD;
  85. const S32 btn_top = BOTTOM_PAD + gBtnHeight + VPAD;
  86. LLFontGL* fontp = LLFontGL::getFontSansSerif();
  87. mAnimating = sGroupNotifyBoxCount <= 0 &&
  88. LLNotifyBox::getNotifyBoxCount() <= 0;
  89. time_t timestamp = (time_t)time_stamp.secondsSinceEpoch();
  90. if (!timestamp)
  91. {
  92. time(&timestamp);
  93. }
  94. if (mHasInventory)
  95. {
  96. mInventoryOffer = new LLOfferInfo(inventory_offer);
  97. }
  98. setFocusRoot(true);
  99. setFollows(FOLLOWS_TOP | FOLLOWS_RIGHT);
  100. setBackgroundVisible(true);
  101. setBackgroundOpaque(true);
  102. setBackgroundColor(gColors.getColor("GroupNotifyBoxColor"));
  103. LLIconCtrl* icon;
  104. LLTextEditor* text;
  105. S32 y = top;
  106. S32 x = 2 * HPAD;
  107. class NoticeText : public LLTextBox
  108. {
  109. public:
  110. NoticeText(const std::string& name, const LLRect& rect,
  111. const std::string& text = LLStringUtil::null,
  112. const LLFontGL* font = NULL)
  113. : LLTextBox(name, rect, text, font)
  114. {
  115. setHAlign(LLFontGL::RIGHT);
  116. setFontStyle(LLFontGL::DROP_SHADOW_SOFT);
  117. setBorderVisible(false);
  118. setColor(gColors.getColor("GroupNotifyTextColor"));
  119. setBackgroundColor(gColors.getColor("GroupNotifyBoxColor"));
  120. }
  121. };
  122. // Title. *TODO: translate
  123. addChild(new NoticeText("title",
  124. LLRect(x, y, right - HPAD, y - LINE_HEIGHT), "Group notice",
  125. LLFontGL::getFontSansSerifHuge()));
  126. y -= llfloor(1.5f * LINE_HEIGHT);
  127. x += 2 * HPAD + ICON_WIDTH;
  128. std::stringstream from;
  129. from << "Sent by " << from_name << ", " << group_name;
  130. addChild(new NoticeText("group",
  131. LLRect(x, y, right - HPAD, y - LINE_HEIGHT),
  132. from.str(), fontp));
  133. y -= LINE_HEIGHT + VPAD;
  134. x = 2 * HPAD;
  135. // *TODO: change this to be the group icon.
  136. if (group_insignia.notNull())
  137. {
  138. icon = new LLIconCtrl("icon",
  139. LLRect(x, y, x + ICON_WIDTH, y - ICON_WIDTH),
  140. group_insignia);
  141. }
  142. else
  143. {
  144. icon = new LLIconCtrl("icon",
  145. LLRect(x, y, x + ICON_WIDTH, y - ICON_WIDTH),
  146. "notify_box_icon.tga");
  147. }
  148. icon->setMouseOpaque(false);
  149. addChild(icon);
  150. x += 2 * HPAD + ICON_WIDTH;
  151. // If we have inventory with this message, leave room for the name.
  152. S32 box_bottom = btn_top + (mHasInventory ? LINE_HEIGHT + 2 * VPAD : 0);
  153. text = new LLViewerTextEditor("box", LLRect(x, y, right, box_bottom),
  154. DB_GROUP_NOTICE_MSG_STR_LEN,
  155. LLStringUtil::null, fontp, false);
  156. static const LLStyleSP headerstyle(new LLStyle(true, LLColor4::black,
  157. "SansSerifBig"));
  158. static const LLStyleSP datestyle(new LLStyle(true, LLColor4::black,
  159. "serif"));
  160. text->appendStyledText(subject + "\n", false, false, headerstyle);
  161. text->appendStyledText(formatted_time(timestamp), false, false, datestyle);
  162. // Sadly, our LLTextEditor cannot handle both styled and unstyled text
  163. // at the same time. Hence this space must be styled. JC
  164. text->appendColoredText(" ", false, false, LLColor4::grey4);
  165. text->setParseHTML(true);
  166. text->appendColoredText("\n\n" + message, false, false,
  167. LLUI::sTextDefaultColor);
  168. LLColor4 bg_color = LLColor4(gColors.getColor("GroupNotifyTextBgColor"));
  169. text->setCursor(0,0);
  170. text->setEnabled(false);
  171. text->setWordWrap(true);
  172. //text->setTabStop(false); // Was interfering with copy-and-paste
  173. text->setTabsToNextField(true);
  174. text->setMouseOpaque(true);
  175. text->setBorderVisible(true);
  176. text->setHideScrollbarForShortDocs(true);
  177. text->setReadOnlyBgColor(bg_color);
  178. text->setWriteableBgColor(bg_color);
  179. addChild(text);
  180. y = box_bottom - VPAD;
  181. if (mHasInventory)
  182. {
  183. addChild(new NoticeText("subjecttitle",
  184. LLRect(x, y, x + LABEL_WIDTH, y - LINE_HEIGHT),
  185. "Attached: ", fontp));
  186. LLUIImagePtr item_icon =
  187. LLInventoryIcon::getIcon(mInventoryOffer->mType,
  188. LLInventoryType::IT_TEXTURE, 0,
  189. false);
  190. x += LABEL_WIDTH + HPAD;
  191. std::stringstream ss;
  192. ss << " " << inventory_name;
  193. LLTextBox* line = new LLTextBox("object_name",
  194. LLRect(x, y, right - HPAD,
  195. y - LINE_HEIGHT),
  196. ss.str(), fontp);
  197. line->setEnabled(false);
  198. line->setBorderVisible(true);
  199. line->setDisabledColor(LLColor4::blue4);
  200. line->setFontStyle(LLFontGL::NORMAL);
  201. line->setBackgroundVisible(true);
  202. line->setBackgroundColor(bg_color);
  203. addChild(line);
  204. icon = new LLIconCtrl("icon", LLRect(x, y, x + 16, y - 16),
  205. item_icon->getName());
  206. icon->setMouseOpaque(false);
  207. addChild(icon);
  208. }
  209. LLRect btn_rect(getRect().getWidth() - 26, BOTTOM_PAD + 20,
  210. getRect().getWidth() - 2, BOTTOM_PAD);
  211. LLButton* btn =
  212. new LLButton("next", btn_rect, "notify_next.png", "notify_next.png",
  213. NULL, onClickNext, this, fontp);
  214. btn->setToolTip("Next"); // *TODO: Translate
  215. btn->setScaleImage(true);
  216. addChild(btn);
  217. mNextBtn = btn;
  218. S32 btn_width = 80;
  219. S32 wide_btn_width = 120;
  220. x = 3 * HPAD;
  221. btn_rect.setOriginAndSize(x, BOTTOM_PAD, btn_width, gBtnHeight);
  222. btn = new LLButton("OK", btn_rect, NULL, onClickOk, this);
  223. addChild(btn, -1);
  224. setDefaultBtn(btn);
  225. x += btn_width + HPAD;
  226. btn_rect.setOriginAndSize(x, BOTTOM_PAD, wide_btn_width, gBtnHeight);
  227. // *TODO: Translate
  228. btn = new LLButton("Group notices", btn_rect, NULL,
  229. onClickGroupInfo, this);
  230. btn->setToolTip("View past notices or opt-out of receiving these messages here.");
  231. addChild(btn, -1);
  232. if (mHasInventory)
  233. {
  234. x += wide_btn_width + HPAD;
  235. btn_rect.setOriginAndSize(x, BOTTOM_PAD, wide_btn_width, gBtnHeight);
  236. std::string btn_lbl;
  237. if (is_openable(mInventoryOffer->mType))
  238. {
  239. btn_lbl = "Open attachment";
  240. }
  241. else
  242. {
  243. btn_lbl = "Save attachment";
  244. }
  245. mSaveInventoryBtn = new LLButton(btn_lbl, btn_rect, NULL,
  246. onClickSaveInventory, this);
  247. mSaveInventoryBtn->setVisible(mHasInventory);
  248. addChild(mSaveInventoryBtn);
  249. }
  250. ++sGroupNotifyBoxCount;
  251. }
  252. //virtual
  253. LLGroupNotifyBox::~LLGroupNotifyBox()
  254. {
  255. --sGroupNotifyBoxCount;
  256. }
  257. //virtual
  258. bool LLGroupNotifyBox::handleRightMouseDown(S32, S32, MASK)
  259. {
  260. moveToBack();
  261. return true;
  262. }
  263. //virtual
  264. void LLGroupNotifyBox::draw()
  265. {
  266. if (!LLNotifyBox::areNotificationsShown())
  267. {
  268. setVisible(false);
  269. return;
  270. }
  271. if (mNextBtn)
  272. {
  273. mNextBtn->setVisible(sGroupNotifyBoxCount > 1);
  274. }
  275. F32 display_time = mTimer.getElapsedTimeF32();
  276. if (mAnimating && display_time < ANIMATION_TIME)
  277. {
  278. gGL.matrixMode(LLRender::MM_MODELVIEW);
  279. gGL.pushMatrix();
  280. S32 height = getRect().getHeight();
  281. F32 fraction = display_time / ANIMATION_TIME;
  282. F32 voffset = (1.f - fraction) * height;
  283. gGL.translatef(0.f, voffset, 0.f);
  284. LLPanel::draw();
  285. gGL.popMatrix();
  286. }
  287. else
  288. {
  289. mAnimating = false;
  290. LLPanel::draw();
  291. }
  292. }
  293. void LLGroupNotifyBox::close()
  294. {
  295. // The group notice dialog may be an inventory offer. If it has an
  296. // inventory save button and that button is still enabled. Then we need to
  297. // send the inventory declined message.
  298. if (mHasInventory)
  299. {
  300. mInventoryOffer->forceResponse(IOR_DECLINE);
  301. mInventoryOffer = NULL;
  302. mHasInventory = false;
  303. }
  304. if (gNotifyBoxViewp)
  305. {
  306. gNotifyBoxViewp->removeChild(this);
  307. }
  308. die();
  309. }
  310. //static
  311. void LLGroupNotifyBox::initClass()
  312. {
  313. LLNotificationChannel::buildChannel("Group Notifications", "Visible",
  314. LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "groupnotify"));
  315. gNotifications.getChannel("Group Notifications")->connectChanged(&LLGroupNotifyBox::onNewNotification);
  316. }
  317. //static
  318. bool LLGroupNotifyBox::onNewNotification(const LLSD& notify)
  319. {
  320. LLNotificationPtr notif = gNotifications.find(notify["id"].asUUID());
  321. if (notif && gNotifyBoxViewp)
  322. {
  323. const LLSD& payload = notif->getPayload();
  324. // Get the group data
  325. LLGroupData group_data;
  326. if (!gAgent.getGroupData(payload["group_id"].asUUID(),group_data))
  327. {
  328. llwarns << "Group notice for unknown group: "
  329. << payload["group_id"].asUUID() << llendl;
  330. return false;
  331. }
  332. LLGroupNotifyBox* self =
  333. new LLGroupNotifyBox(payload["subject"].asString(),
  334. payload["message"].asString(),
  335. payload["sender_name"].asString(),
  336. payload["group_id"].asUUID(),
  337. group_data.mInsigniaID,
  338. group_data.mName, notif->getDate(),
  339. payload["inventory_offer"].isDefined(),
  340. payload["inventory_name"].asString(),
  341. payload["inventory_offer"]);
  342. gNotifyBoxViewp->addChild(self);
  343. }
  344. return false;
  345. }
  346. void LLGroupNotifyBox::moveToBack()
  347. {
  348. if (!gNotifyBoxViewp) return;
  349. // Move this dialog to the back.
  350. gNotifyBoxViewp->removeChild(this);
  351. gNotifyBoxViewp->addChildAtEnd(this);
  352. }
  353. //static
  354. LLRect LLGroupNotifyBox::getGroupNotifyRect()
  355. {
  356. static LLCachedControl<S32> notify_height(gSavedSettings,
  357. "GroupNotifyBoxHeight");
  358. S32 height = notify_height;
  359. if (height < 150)
  360. {
  361. height = 150;
  362. }
  363. static LLCachedControl<S32> notify_width(gSavedSettings,
  364. "GroupNotifyBoxWidth");
  365. S32 width = notify_width;
  366. if (width < 250)
  367. {
  368. width = 250;
  369. }
  370. S32 top, right;
  371. if (gNotifyBoxViewp)
  372. {
  373. top = gNotifyBoxViewp->getRect().getHeight();
  374. right = gNotifyBoxViewp->getRect().getWidth();
  375. }
  376. else
  377. {
  378. top = height;
  379. right = 0;
  380. }
  381. return LLRect(right - width, top, right, top - height);
  382. }
  383. //static
  384. void LLGroupNotifyBox::onClickOk(void* data)
  385. {
  386. LLGroupNotifyBox* self = (LLGroupNotifyBox*)data;
  387. if (self)
  388. {
  389. self->close();
  390. }
  391. }
  392. void LLGroupNotifyBox::onClickGroupInfo(void* data)
  393. {
  394. LLGroupNotifyBox* self = (LLGroupNotifyBox*)data;
  395. if (self)
  396. {
  397. LLFloaterGroupInfo::showFromUUID(self->mGroupID, "notices_tab");
  398. }
  399. // Leave notice open until explicitly closed
  400. }
  401. void LLGroupNotifyBox::onClickSaveInventory(void* data)
  402. {
  403. LLGroupNotifyBox* self = (LLGroupNotifyBox*)data;
  404. if (!self) return;
  405. self->mInventoryOffer->forceResponse(IOR_ACCEPT);
  406. self->mInventoryOffer = NULL;
  407. self->mHasInventory = false;
  408. // Each item can only be received once, so disable the button.
  409. self->mSaveInventoryBtn->setEnabled(false);
  410. }
  411. //static
  412. void LLGroupNotifyBox::onClickNext(void* data)
  413. {
  414. LLGroupNotifyBox* self = (LLGroupNotifyBox*)data;
  415. if (self)
  416. {
  417. self->moveToBack();
  418. }
  419. }