phoenix-firestorm/indra/newview/lltoastnotifypanel.cpp

435 lines
12 KiB
C++

/**
* @file lltoastnotifypanel.cpp
* @brief Panel for notify toasts.
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* 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.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lltoastnotifypanel.h"
#include "llviewercontrol.h"
#include "lluiconstants.h"
#include "llrect.h"
#include "lliconctrl.h"
#include "lltexteditor.h"
#include "lltextbox.h"
#include "lldbstrings.h"
#include "llchat.h"
#include "llfloaterchat.h"
#include "lltrans.h"
#include "lloverlaybar.h"
const S32 BOTTOM_PAD = VPAD * 3;
//static
const LLFontGL* LLToastNotifyPanel::sFont = NULL;
const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL;
LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToastPanel(notification) {
mIsTip = notification->getType() == "notifytip";
mNumOptions = 0;
mNumButtons = 0;
mIsScriptDialog = (notification->getName() == "ScriptDialog"
|| notification->getName() == "ScriptDialogGroup");
mAddedDefaultBtn = false;
// clicking on a button does not steal current focus
setIsChrome(TRUE);
// class init
if (!sFont)
{
sFont = LLFontGL::getFontSansSerif();
sFontSmall = LLFontGL::getFontSansSerifSmall();
}
// setup paramaters
mMessage = notification->getMessage();
// initialize
setFocusRoot(!mIsTip);
// caution flag can be set explicitly by specifying it in the
// notification payload, or it can be set implicitly if the
// notify xml template specifies that it is a caution
//
// tip-style notification handle 'caution' differently -
// they display the tip in a different color
mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH;
LLNotificationFormPtr form(notification->getForm());
mNumOptions = form->getNumElements();
LLRect rect = mIsTip ? getNotifyTipRect(mMessage)
: getNotifyRect(mNumOptions, mIsScriptDialog, mIsCaution);
setRect(rect);
setFollows(mIsTip ? (FOLLOWS_BOTTOM|FOLLOWS_RIGHT) : (FOLLOWS_TOP|FOLLOWS_RIGHT));
setBackgroundVisible(FALSE);
setBackgroundOpaque(TRUE);
LLIconCtrl* icon;
LLTextEditor* text;
const S32 TOP = getRect().getHeight() - (mIsTip ? (S32)sFont->getLineHeight() : 32);
const S32 BOTTOM = (S32)sFont->getLineHeight();
S32 x = HPAD + HPAD;
S32 y = TOP;
LLIconCtrl::Params common_params;
common_params.rect(LLRect(x, y, x+32, TOP-32));
common_params.mouse_opaque(false);
common_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
if (mIsTip)
{
// use the tip notification icon
common_params.image(LLUI::getUIImage("notify_tip_icon.tga"));
icon = LLUICtrlFactory::create<LLIconCtrl> (common_params);
}
else if (mIsCaution)
{
// use the caution notification icon
common_params.image(LLUI::getUIImage("notify_caution_icon.tga"));
icon = LLUICtrlFactory::create<LLIconCtrl> (common_params);
}
else
{
// use the default notification icon
common_params.image(LLUI::getUIImage("notify_box_icon.tga"));
icon = LLUICtrlFactory::create<LLIconCtrl> (common_params);
}
icon->setMouseOpaque(FALSE);
addChild(icon);
x += HPAD + HPAD + 32;
// add a caution textbox at the top of a caution notification
LLTextBox* caution_box = NULL;
if (mIsCaution && !mIsTip)
{
S32 caution_height = ((S32)sFont->getLineHeight() * 2) + VPAD;
LLTextBox::Params params;
params.name("caution_box");
params.rect(LLRect(x, y, getRect().getWidth() - 2, caution_height));
params.font(sFont);
params.mouse_opaque(false);
params.font.style("BOLD");
params.text_color(LLUIColorTable::instance().getColor("NotifyCautionWarnColor"));
params.bg_readonly_color(LLUIColorTable::instance().getColor("NotifyCautionBoxColor"));
params.border_visible(false);
params.wrap(true);
caution_box = LLUICtrlFactory::create<LLTextBox> (params);
caution_box->setValue(notification->getMessage());
addChild(caution_box);
// adjust the vertical position of the next control so that
// it appears below the caution textbox
y = y - caution_height;
}
else
{
const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD);
// Tokenization on \n is handled by LLTextBox
const S32 MAX_LENGTH = 512 + 20 +
DB_FIRST_NAME_BUF_SIZE +
DB_LAST_NAME_BUF_SIZE +
DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title.
LLTextEditor::Params params;
params.name("box");
params.rect(LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16));
params.max_text_length(MAX_LENGTH);
params.read_only(true);
params.default_text(mMessage);
params.font(sFont);
params.embedded_items(false);
params.wrap(true);
params.tab_stop(false);
params.mouse_opaque(false);
params.bg_readonly_color(LLColor4::transparent);
params.text_readonly_color(LLUIColorTable::instance().getColor("NotifyTextColor"));
params.enabled(false);
params.border_visible(false);
text = LLUICtrlFactory::create<LLTextEditor> (params);
addChild(text);
}
if (mIsTip)
{
// TODO: Make a separate archive for these.
LLChat chat(mMessage);
chat.mSourceType = CHAT_SOURCE_SYSTEM;
LLFloaterChat::addChatHistory(chat);
}
else
{
LLButton::Params p;
p.name(std::string("next"));
p.rect(LLRect(getRect().getWidth()-26, BOTTOM_PAD + 20, getRect().getWidth()-2, BOTTOM_PAD));
p.image_selected.name("notify_next.png");
p.image_unselected.name("notify_next.png");
p.font(sFont);
p.scale_image(true);
p.tool_tip(LLTrans::getString("next").c_str());
for (S32 i = 0; i < mNumOptions; i++)
{
LLSD form_element = form->getElement(i);
if (form_element["type"].asString() != "button")
{
continue;
}
addButton(form_element["name"].asString(), form_element["text"].asString(), TRUE, form_element["default"].asBoolean());
}
if (mNumButtons == 0)
{
addButton("OK", LLTrans::getString("ok"), FALSE, TRUE);
mAddedDefaultBtn = true;
}
}
}
LLToastNotifyPanel::~LLToastNotifyPanel() {
std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer());
}
LLRect LLToastNotifyPanel::getNotifyRect(S32 num_options, BOOL mIsScriptDialog, BOOL is_caution)
{
S32 notify_height = gSavedSettings.getS32("NotifyBoxHeight");
if (is_caution)
{
// make caution-style dialog taller to accomodate extra text,
// as well as causing the accept/decline buttons to be drawn
// in a different position, to help prevent "quick-click-through"
// of many permissions prompts
notify_height = gSavedSettings.getS32("PermissionsCautionNotifyBoxHeight");
}
const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth");
const S32 TOP = getRect().getHeight();
const S32 RIGHT =getRect().getWidth();
const S32 LEFT = RIGHT - NOTIFY_WIDTH;
if (num_options < 1)
{
num_options = 1;
}
// Add two "blank" option spaces.
if (mIsScriptDialog)
{
num_options += 2;
}
S32 additional_lines = (num_options-1) / 3;
notify_height += additional_lines * (BTN_HEIGHT + VPAD);
return LLRect(LEFT, TOP, RIGHT, TOP-notify_height);
}
// static
LLRect LLToastNotifyPanel::getNotifyTipRect(const std::string &utf8message)
{
S32 line_count = 1;
LLWString message = utf8str_to_wstring(utf8message);
S32 message_len = message.length();
const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth");
// Make room for the icon area.
const S32 text_area_width = NOTIFY_WIDTH - HPAD * 4 - 32;
const llwchar* wchars = message.c_str();
const llwchar* start = wchars;
const llwchar* end;
S32 total_drawn = 0;
BOOL done = FALSE;
do
{
line_count++;
for (end=start; *end != 0 && *end != '\n'; end++)
;
if( *end == 0 )
{
end = wchars + message_len;
done = TRUE;
}
S32 remaining = end - start;
while( remaining )
{
S32 drawn = sFont->maxDrawableChars( start, (F32)text_area_width, remaining, TRUE );
if( 0 == drawn )
{
drawn = 1; // Draw at least one character, even if it doesn't all fit. (avoids an infinite loop)
}
total_drawn += drawn;
start += drawn;
remaining -= drawn;
if( total_drawn < message_len )
{
if( (wchars[ total_drawn ] != '\n') )
{
// wrap because line was too long
line_count++;
}
}
else
{
done = TRUE;
}
}
total_drawn++; // for '\n'
end++;
start = end;
} while( !done );
const S32 MIN_NOTIFY_HEIGHT = 72;
const S32 MAX_NOTIFY_HEIGHT = 600;
S32 notify_height = llceil((F32) (line_count+1) * sFont->getLineHeight());
if(gOverlayBar)
{
notify_height += gOverlayBar->getBoundingRect().mTop;
}
else
{
// *FIX: this is derived from the padding caused by the
// rounded rects, shouldn't be a const here.
notify_height += 10;
}
notify_height += VPAD;
notify_height = llclamp(notify_height, MIN_NOTIFY_HEIGHT, MAX_NOTIFY_HEIGHT);
const S32 RIGHT = getRect().getWidth();
const S32 LEFT = RIGHT - NOTIFY_WIDTH;
return LLRect(LEFT, notify_height, RIGHT, 0);
}
// static
void LLToastNotifyPanel::onClickButton(void* data)
{
InstanceAndS32* self_and_button = (InstanceAndS32*)data;
LLToastNotifyPanel* self = self_and_button->mSelf;
std::string button_name = self_and_button->mButtonName;
LLSD response = self->mNotification->getResponseTemplate();
if (!self->mAddedDefaultBtn && !button_name.empty())
{
response[button_name] = true;
}
self->mNotification->respond(response);
}
// virtual
LLButton* LLToastNotifyPanel::addButton(const std::string& name, const std::string& label, BOOL is_option, BOOL is_default)
{
// make caution notification buttons slightly narrower
// so that 3 of them can fit without overlapping the "next" button
S32 btn_width = mIsCaution? 84 : 90;
LLRect btn_rect;
LLButton* btn;
S32 btn_height= BTN_HEIGHT;
const LLFontGL* font = sFont;
S32 ignore_pad = 0;
S32 button_index = mNumButtons;
S32 index = button_index;
S32 x = (HPAD * 4) + 32;
if (mIsScriptDialog)
{
// Add two "blank" option spaces, before the "Ignore" button
index = button_index + 2;
if (button_index == 0)
{
// Ignore button is smaller, less wide
btn_height = BTN_HEIGHT_SMALL;
font = sFontSmall;
ignore_pad = 10;
}
}
btn_rect.setOriginAndSize(x + (index % 3) * (btn_width+HPAD+HPAD) + ignore_pad,
BOTTOM_PAD + (index / 3) * (BTN_HEIGHT+VPAD),
btn_width - 2*ignore_pad,
btn_height);
InstanceAndS32* userdata = new InstanceAndS32;
userdata->mSelf = this;
userdata->mButtonName = is_option ? name : "";
mBtnCallbackData.push_back(userdata);
LLButton::Params p;
p.name(name);
p.label(label);
p.rect(btn_rect);
p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata));
p.font(font);
if (mIsCaution)
{
p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
p.image_color_disabled(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
}
btn = LLUICtrlFactory::create<LLButton>(p);
addChild(btn, -1);
if (is_default)
{
setDefaultBtn(btn);
}
mNumButtons++;
return btn;
}