summaryrefslogtreecommitdiff
path: root/indra/newview/lltoastnotifypanel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lltoastnotifypanel.cpp')
-rw-r--r--indra/newview/lltoastnotifypanel.cpp411
1 files changed, 333 insertions, 78 deletions
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index 94acb2ae8c..1a1c94674b 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -2,31 +2,25 @@
* @file lltoastnotifypanel.cpp
* @brief Panel for notify toasts.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,24 +37,35 @@
#include "lluiconstants.h"
#include "llrect.h"
#include "lltrans.h"
+#include "llnotificationsutil.h"
+#include "llviewermessage.h"
+#include "llimfloater.h"
const S32 BOTTOM_PAD = VPAD * 3;
+const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding
S32 BUTTON_WIDTH = 90;
//static
const LLFontGL* LLToastNotifyPanel::sFont = NULL;
const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL;
-LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) :
+LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal;
+
+LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification, const LLRect& rect, bool show_images) :
LLToastPanel(notification),
mTextBox(NULL),
mInfoPanel(NULL),
mControlPanel(NULL),
mNumOptions(0),
mNumButtons(0),
-mAddedDefaultBtn(false)
+mAddedDefaultBtn(false),
+mCloseNotificationOnDestroy(true)
{
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_notification.xml");
+ if(rect != LLRect::null)
+ {
+ this->setShape(rect);
+ }
mInfoPanel = getChild<LLPanel>("info_panel");
mControlPanel = getChild<LLPanel>("control_panel");
BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth");
@@ -115,6 +120,7 @@ mAddedDefaultBtn(false)
mTextBox->setMaxTextLength(MAX_LENGTH);
mTextBox->setVisible(TRUE);
+ mTextBox->setPlainText(!show_images);
mTextBox->setValue(notification->getMessage());
// add buttons for a script notification
@@ -126,14 +132,6 @@ mAddedDefaultBtn(false)
{
std::vector<index_button_pair_t> buttons;
buttons.reserve(mNumOptions);
- for (S32 i = 0; i < mNumOptions; i++)
- {
- LLSD form_element = form->getElement(i);
- if (form_element["type"].asString() != "button")
- {
- continue;
- }
- }
S32 buttons_width = 0;
// create all buttons and accumulate they total width to reshape mControlPanel
for (S32 i = 0; i < mNumOptions; i++)
@@ -154,27 +152,66 @@ mAddedDefaultBtn(false)
}
else
{
- //try get an average left_pad to spread out buttons
- S32 left_pad = (getRect().getWidth() - buttons_width) / (S32(buttons.size() + 1));
- // left_pad can be < 2*HPAD if we have a lot of buttons.
- if(left_pad < 2*HPAD)
+ const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel
+ S32 button_panel_height = mControlPanel->getRect().getHeight();
+ //try get an average h_pad to spread out buttons
+ S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size()));
+ if(h_pad < 2*HPAD)
+ {
+ /*
+ * Probably it is a scriptdialog toast
+ * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons.
+ * In last case set default h_pad to avoid heaping of buttons
+ */
+ S32 button_per_row = button_panel_width / BUTTON_WIDTH;
+ h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row
+ if(h_pad < 2*HPAD) // still not enough space between buttons ?
+ {
+ h_pad = 2*HPAD;
+ }
+ }
+ if (mIsScriptDialog)
{
- //Probably it is a scriptdialog toast, set default left_pad
- left_pad = 2*HPAD;
+ // we are using default width for script buttons so we can determinate button_rows
+ //to get a number of rows we divide the required width of the buttons to button_panel_width
+ S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width);
+ //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width;
+ //reserve one row for the ignore_btn
+ button_rows++;
+ //calculate required panel height for scripdialog notification.
+ button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD;
}
- //how many rows we need to fit all buttons with current width of the panel
- S32 button_rows = (buttons_width + left_pad * S32(buttons.size() + 1)) / getRect().getWidth() + 1;
- //calculate required panel height
- S32 button_panel_height = button_rows *( BTN_HEIGHT + VPAD) + BOTTOM_PAD;
-
- adjustPanelForScriptNotice(getRect().getWidth(), button_panel_height);
- //we begin from lefttop angle and go to rightbottom.
- updateButtonsLayout(buttons, left_pad, button_panel_height);
+ else
+ {
+ // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width
+ //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width);
+ S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width);
+ //calculate required panel height
+ button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD;
+ }
+
+ // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed
+ adjustPanelForScriptNotice(button_panel_width, button_panel_height);
+ updateButtonsLayout(buttons, h_pad);
+ // save buttons for later use in disableButtons()
+ mButtons.assign(buttons.begin(), buttons.end());
}
}
// adjust panel's height to the text size
mInfoPanel->setFollowsAll();
snapToMessageHeight(mTextBox, MAX_LENGTH);
+
+ if(notification->isReusable())
+ {
+ mButtonClickConnection = sButtonClickSignal.connect(
+ boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2));
+
+ if(notification->isRespondedTo())
+ {
+ // User selected an option in toast, now disable required buttons in IM window
+ disableRespondedOptions(notification);
+ }
+ }
}
void LLToastNotifyPanel::addDefaultButton()
{
@@ -201,7 +238,8 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
mBtnCallbackData.push_back(userdata);
LLButton::Params p;
- const LLFontGL* font = form_element["index"].asInteger() == -1 ? sFontSmall: sFont; // for ignore button in script dialog
+ bool is_ignore_btn = form_element["index"].asInteger() == -1;
+ const LLFontGL* font = is_ignore_btn ? sFontSmall: sFont; // for ignore button in script dialog
p.name(form_element["name"].asString());
p.label(form_element["text"].asString());
p.font(font);
@@ -209,18 +247,25 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata));
p.rect.width = BUTTON_WIDTH;
p.auto_resize = false;
- p.follows.flags(FOLLOWS_RIGHT | FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+ p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
if (mIsCaution)
{
p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
p.image_color_disabled(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
}
+ // for the scriptdialog buttons we use fixed button size. This is a limit!
if (!mIsScriptDialog && font->getWidth(form_element["text"].asString()) > BUTTON_WIDTH)
{
p.rect.width = 1;
p.auto_resize = true;
}
-
+ else if (mIsScriptDialog && is_ignore_btn)
+ {
+ // this is ignore button,make it smaller
+ p.rect.height = BTN_HEIGHT_SMALL;
+ p.rect.width = 1;
+ p.auto_resize = true;
+ }
LLButton* btn = LLUICtrlFactory::create<LLButton>(p);
mNumButtons++;
btn->autoResize();
@@ -234,40 +279,61 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
LLToastNotifyPanel::~LLToastNotifyPanel()
{
+ mButtonClickConnection.disconnect();
+
std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer());
+ if (mCloseNotificationOnDestroy && LLNotificationsUtil::find(mNotification->getID()) != NULL)
+ {
+ // let reusable notification be deleted
+ mNotification->setReusable(false);
+ if (!mNotification->isPersistent())
+ {
+ LLNotifications::getInstance()->cancel(mNotification);
+ }
+ }
}
-void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 left_pad, S32 top)
+
+void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 h_pad)
{
- S32 left = left_pad;
+ S32 left = 0;
+ //reserve place for ignore button
+ S32 bottom_offset = mIsScriptDialog ? (BTN_HEIGHT + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD) : BOTTOM_PAD;
+ S32 max_width = mControlPanel->getRect().getWidth();
LLButton* ignore_btn = NULL;
for (std::vector<index_button_pair_t>::const_iterator it = buttons.begin(); it != buttons.end(); it++)
{
- if(left + it->second->getRect().getWidth() + 2*HPAD > getRect().getWidth())
- {
- // looks like we need to add button to the next row
- left = left_pad;
- top-= (BTN_HEIGHT + VPAD);
- }
- LLRect btn_rect(it->second->getRect());
- if(mIsScriptDialog && it->first == -1)
+ if (it->first == -1)
{
- //this is ignore button ( index == -1) we need to add it into new extra row at the end
ignore_btn = it->second;
continue;
}
- btn_rect.setLeftTopAndSize(left, top, btn_rect.getWidth(), btn_rect.getHeight());
- it->second->setRect(btn_rect);
- left = btn_rect.mLeft + btn_rect.getWidth() + left_pad;
- addChild(it->second, -1);
+ LLButton* btn = it->second;
+ LLRect btn_rect(btn->getRect());
+ if (left + btn_rect.getWidth() > max_width)// whether there is still some place for button+h_pad in the mControlPanel
+ {
+ // looks like we need to add button to the next row
+ left = 0;
+ bottom_offset += (BTN_HEIGHT + VPAD);
+ }
+ //we arrange buttons from bottom to top for backward support of old script
+ btn_rect.setOriginAndSize(left, bottom_offset, btn_rect.getWidth(), btn_rect.getHeight());
+ btn->setRect(btn_rect);
+ left = btn_rect.mLeft + btn_rect.getWidth() + h_pad;
+ mControlPanel->addChild(btn, -1);
}
- if(ignore_btn)
+ if (mIsScriptDialog && ignore_btn != NULL)
{
- LLRect btn_rect(ignore_btn->getRect());
- btn_rect.setOriginAndSize(getRect().getWidth() - btn_rect.getWidth() - left_pad,
- BOTTOM_PAD,// move button at the bottom edge
- btn_rect.getWidth(), btn_rect.getHeight());
- ignore_btn->setRect(btn_rect);
- addChild(ignore_btn, -1);
+ LLRect ignore_btn_rect(ignore_btn->getRect());
+ S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH
+ S32 ignore_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - ignore_btn_rect.getWidth();
+ if (ignore_btn_left + ignore_btn_rect.getWidth() > max_width)// make sure that the ignore button is in panel
+ {
+ ignore_btn_left = max_width - ignore_btn_rect.getWidth() - 2 * HPAD;
+ }
+ ignore_btn_rect.setOriginAndSize(ignore_btn_left, BOTTOM_PAD,// always move ignore button at the bottom
+ ignore_btn_rect.getWidth(), ignore_btn_rect.getHeight());
+ ignore_btn->setRect(ignore_btn_rect);
+ mControlPanel->addChild(ignore_btn, -1);
}
}
@@ -275,20 +341,124 @@ void LLToastNotifyPanel::adjustPanelForScriptNotice(S32 button_panel_width, S32
{
//adjust layout
// we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed
- LLRect button_rect = mControlPanel->getRect();
reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight() + button_panel_height + VPAD);
- button_rect.set(0, button_rect.mBottom + button_panel_height, button_rect.getWidth(), button_rect.mBottom);
- mControlPanel->reshape(button_rect.getWidth(), button_panel_height);
- mControlPanel->setRect(button_rect);
+ mControlPanel->reshape( button_panel_width, button_panel_height);
}
void LLToastNotifyPanel::adjustPanelForTipNotice()
{
LLRect info_rect = mInfoPanel->getRect();
LLRect this_rect = getRect();
-
+ //we don't need display ControlPanel for tips because they doesn't contain any buttons.
mControlPanel->setVisible(FALSE);
reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight());
+
+ if (mNotification->getPayload().has("respond_on_mousedown")
+ && mNotification->getPayload()["respond_on_mousedown"] )
+ {
+ mInfoPanel->setMouseDownCallback(
+ boost::bind(&LLNotification::respond,
+ mNotification,
+ mNotification->getResponseTemplate()));
+ }
+}
+
+typedef std::set<std::string> button_name_set_t;
+typedef std::map<std::string, button_name_set_t> disable_button_map_t;
+
+disable_button_map_t initUserGiveItemDisableButtonMap()
+{
+ // see EXT-5905 for disable rules
+
+ disable_button_map_t disable_map;
+ button_name_set_t buttons;
+
+ buttons.insert("Show");
+ disable_map.insert(std::make_pair("Show", buttons));
+
+ buttons.insert("Discard");
+ disable_map.insert(std::make_pair("Discard", buttons));
+
+ buttons.insert("Mute");
+ disable_map.insert(std::make_pair("Mute", buttons));
+
+ return disable_map;
+}
+
+disable_button_map_t initTeleportOfferedDisableButtonMap()
+{
+ disable_button_map_t disable_map;
+ button_name_set_t buttons;
+
+ buttons.insert("Teleport");
+ buttons.insert("Cancel");
+
+ disable_map.insert(std::make_pair("Teleport", buttons));
+ disable_map.insert(std::make_pair("Cancel", buttons));
+
+ return disable_map;
+}
+
+disable_button_map_t initFriendshipOfferedDisableButtonMap()
+{
+ disable_button_map_t disable_map;
+ button_name_set_t buttons;
+
+ buttons.insert("Accept");
+ buttons.insert("Decline");
+
+ disable_map.insert(std::make_pair("Accept", buttons));
+ disable_map.insert(std::make_pair("Decline", buttons));
+
+ return disable_map;
+}
+
+button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name)
+{
+ static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap();
+ static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap();
+ static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap();
+
+ disable_button_map_t::const_iterator it;
+ disable_button_map_t::const_iterator it_end;
+ disable_button_map_t search_map;
+
+ if("UserGiveItem" == notification_name)
+ {
+ search_map = user_give_item_disable_map;
+ }
+ else if("TeleportOffered" == notification_name)
+ {
+ search_map = teleport_offered_disable_map;
+ }
+ else if("OfferFriendship" == notification_name)
+ {
+ search_map = friendship_offered_disable_map;
+ }
+
+ it = search_map.find(button_name);
+ it_end = search_map.end();
+
+ if(it_end != it)
+ {
+ return it->second;
+ }
+ return button_name_set_t();
+}
+
+void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button)
+{
+ button_name_set_t buttons = getButtonDisableList(notification_name, selected_button);
+
+ std::vector<index_button_pair_t>::const_iterator it = mButtons.begin();
+ for ( ; it != mButtons.end(); it++)
+ {
+ LLButton* btn = it->second;
+ if(buttons.find(btn->getName()) != buttons.end())
+ {
+ btn->setEnabled(FALSE);
+ }
+ }
}
// static
@@ -303,5 +473,90 @@ void LLToastNotifyPanel::onClickButton(void* data)
{
response[button_name] = true;
}
+
+ bool is_reusable = self->mNotification->isReusable();
+ // When we call respond(), LLOfferInfo will delete itself in inventory_offer_callback(),
+ // lets copy it while it's still valid.
+ LLOfferInfo* old_info = static_cast<LLOfferInfo*>(self->mNotification->getResponder());
+ LLOfferInfo* new_info = NULL;
+ if(is_reusable && old_info)
+ {
+ new_info = new LLOfferInfo(*old_info);
+ self->mNotification->setResponder(new_info);
+ }
+
self->mNotification->respond(response);
+
+ if(is_reusable)
+ {
+ sButtonClickSignal(self->mNotification->getID(), button_name);
+ }
+ else
+ {
+ // disable all buttons
+ self->mControlPanel->setEnabled(FALSE);
+ }
+}
+
+void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name)
+{
+ if(mNotification->getID() == notification_id)
+ {
+ disableButtons(mNotification->getName(), btn_name);
+ }
+}
+
+void LLToastNotifyPanel::disableRespondedOptions(LLNotificationPtr& notification)
+{
+ LLSD response = notification->getResponse();
+ for (LLSD::map_const_iterator response_it = response.beginMap();
+ response_it != response.endMap(); ++response_it)
+ {
+ if (response_it->second.isBoolean() && response_it->second.asBoolean())
+ {
+ // that after multiple responses there can be many pressed buttons
+ // need to process them all
+ disableButtons(notification->getName(), response_it->first);
+ }
+ }
}
+
+
+//////////////////////////////////////////////////////////////////////////
+
+LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */,
+ bool show_images /* = true */)
+ : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images)
+{
+ mTextBox->setFollowsAll();
+}
+
+LLIMToastNotifyPanel::~LLIMToastNotifyPanel()
+{
+ // We shouldn't delete notification when IM floater exists
+ // since that notification will be reused by IM floater.
+ // This may happened when IM floater reloads messages, exactly when user
+ // changes layout of IM chat log(disable/enable plaintext mode).
+ // See EXT-6500
+ LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID);
+ if (im_floater != NULL && !im_floater->isDead())
+ {
+ mCloseNotificationOnDestroy = false;
+ }
+}
+
+void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ S32 text_height = mTextBox->getTextBoundingRect().getHeight();
+ S32 widget_height = mTextBox->getRect().getHeight();
+ S32 delta = text_height - widget_height;
+ LLRect rc = getRect();
+
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta);
+ height = rc.getHeight();
+ width = rc.getWidth();
+
+ LLToastPanel::reshape(width, height, called_from_parent);
+}
+
+// EOF