123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- /**
- * @file llalertdialog.cpp
- * @brief LLAlertDialog base class
- *
- * $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 "linden_common.h"
- #include "llalertdialog.h"
- #include "llbutton.h"
- #include "llcheckboxctrl.h"
- #include "llkeyboard.h"
- #include "llfunctorregistry.h"
- #include "lliconctrl.h"
- #include "lllineeditor.h"
- #include "llnotifications.h"
- #include "lltextbox.h"
- #include "lluictrlfactory.h"
- #include "llxmlnode.h"
- constexpr S32 MAX_ALLOWED_MSG_WIDTH = 400;
- constexpr F32 DEFAULT_BUTTON_DELAY = 0.5f;
- constexpr S32 DIALOG_VPAD = 16;
- constexpr S32 DIALOG_HPAD = 25;
- constexpr S32 BTN_HPAD = 8;
- // Static members
- LLControlGroup* LLAlertDialog::sSettings = NULL;
- LLAlertDialog::URLLoader* LLAlertDialog::sURLLoader = NULL;
- LLAlertDialog::lua_alert_cb_t LLAlertDialog::sLuaCallback = NULL;
- //static
- void LLAlertDialog::initClass()
- {
- LLNotificationChannel::buildChannel("Alerts", "Visible",
- LLNotificationFilters::filterBy<std::string>(&LLNotification::getType,
- "alert"));
- LLNotificationChannel::buildChannel("AlertModal", "Visible",
- LLNotificationFilters::filterBy<std::string>(&LLNotification::getType,
- "alertmodal"));
- gNotifications.getChannel("Alerts")->connectChanged(boost::bind(&onNewNotification,
- _1,
- false));
- gNotifications.getChannel("AlertModal")->connectChanged(boost::bind(&onNewNotification,
- _1,
- true));
- }
- //static
- bool LLAlertDialog::onNewNotification(const LLSD& notify, bool is_modal)
- {
- LLNotificationPtr notif = gNotifications.find(notify["id"].asUUID());
- if (notif)
- {
- if (notify["sigtype"].asString() == "add" ||
- notify["sigtype"].asString() == "load")
- {
- LLAlertDialog* dialog = new LLAlertDialog(notif, is_modal);
- dialog->show();
- }
- else if (notify["sigtype"].asString() == "change")
- {
- LLAlertDialog* dialog = getNamedInstance(notif->getID()).get();
- if (dialog)
- {
- dialog->show();
- }
- else
- {
- LLAlertDialog* dialog = new LLAlertDialog(notif, is_modal);
- dialog->show();
- }
- }
- }
- return false;
- }
- LLAlertDialog::LLAlertDialog(LLNotificationPtr notification, bool modal)
- // dummy size, will reshape below
- : LLModalDialog(notification->getLabel(), 100, 100, modal),
- LLInstanceTracker<LLAlertDialog, LLUUID>(notification->getID()),
- mDefaultOption(0),
- mCheck(NULL),
- mCaution(notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH),
- mLabel(notification->getName()),
- mLineEditor(NULL),
- mNote(notification)
- {
- mFont = LLFontGL::getFontSansSerif();
- const S32 line_height = llfloor(mFont->getLineHeight() + 0.99f);
- constexpr S32 EDITOR_HEIGHT = 20;
- LLNotificationFormPtr form = mNote->getForm();
- std::string edit_text_name;
- std::string edit_text_contents;
- bool is_password = false;
- setBackgroundVisible(true);
- setBackgroundOpaque(true);
- typedef std::vector<std::pair<std::string, std::string> > options_t;
- options_t supplied_options;
- // For now, get LLSD to iterator over form elements
- LLSD form_sd = form->asLLSD();
- S32 option_index = 0;
- for (LLSD::array_const_iterator it = form_sd.beginArray();
- it != form_sd.endArray(); ++it)
- {
- std::string type = (*it)["type"].asString();
- if (type == "button")
- {
- if ((*it)["default"])
- {
- mDefaultOption = option_index;
- }
- supplied_options.emplace_back((*it)["name"].asString(),
- (*it)["text"].asString());
- if (option_index == mNote->getURLOption())
- {
- mButtonData.emplace_back(this, nullptr, mNote->getURL());
- }
- else
- {
- mButtonData.emplace_back(this);
- }
- ++option_index;
- }
- else if (type == "text")
- {
- edit_text_contents = (*it)["value"].asString();
- edit_text_name = (*it)["name"].asString();
- }
- else if (type == "password")
- {
- edit_text_contents = (*it)["value"].asString();
- edit_text_name = (*it)["name"].asString();
- is_password = true;
- }
- }
- // Buttons
- options_t options;
- if (supplied_options.empty())
- {
- options.emplace_back("close", "Close");
- // Add data for ok button.
- mButtonData.emplace_back(this);
- mDefaultOption = 0;
- }
- else
- {
- options = supplied_options;
- }
- S32 num_options = options.size();
- // Calc total width of buttons
- S32 button_width = 0;
- S32 sp = mFont->getWidth("OO");
- for (S32 i = 0; i < num_options; ++i)
- {
- S32 w = S32(mFont->getWidth(options[i].second) + 0.99f) + sp +
- 2 * gButtonHPad;
- button_width = llmax(w, button_width);
- }
- S32 btn_total_width = button_width;
- if (num_options > 1)
- {
- btn_total_width = num_options * button_width +
- (num_options - 1) * BTN_HPAD;
- }
- // Message: create text box using raw string, as text has been structured
- // deliberately. Use size of created text box to generate dialog box size.
- std::string msg = mNote->getMessage();
- llwarns << "Alert: " << msg << llendl;
- LLTextBox* msg_box = new LLTextBox("Alert message", msg,
- (F32)MAX_ALLOWED_MSG_WIDTH, mFont);
- const LLRect& text_rect = msg_box->getRect();
- S32 dialog_width = llmax(btn_total_width, text_rect.getWidth()) +
- 2 * DIALOG_HPAD;
- S32 dialog_height = text_rect.getHeight() + 3 * DIALOG_VPAD + gBtnHeight;
- if (hasTitleBar())
- {
- dialog_height += line_height; // room for title bar
- }
- // It's ok for the edit text body to be empty, but we want the name to
- // exist if we're going to draw it
- if (!edit_text_name.empty())
- {
- dialog_height += EDITOR_HEIGHT + DIALOG_VPAD;
- dialog_width = llmax(dialog_width,
- (S32)(mFont->getWidth(edit_text_contents) + 0.99f));
- }
- if (mCaution)
- {
- // Make room for the caution icon.
- dialog_width += 32 + DIALOG_HPAD;
- }
- reshape(dialog_width, dialog_height, false);
- S32 msg_y = getRect().getHeight() - DIALOG_VPAD;
- S32 msg_x = DIALOG_HPAD;
- if (hasTitleBar())
- {
- msg_y -= line_height; // room for title
- }
- if (mCaution)
- {
- LLIconCtrl* icon =
- new LLIconCtrl("icon", LLRect(msg_x, msg_y, msg_x + 32, msg_y - 32),
- "notify_caution_icon.tga");
- icon->setMouseOpaque(false);
- addChild(icon);
- msg_x += 32 + DIALOG_HPAD;
- msg_box->setColor(LLUI::sColorsGroup->getColor("AlertCautionTextColor"));
- }
- else
- {
- msg_box->setColor(LLUI::sColorsGroup->getColor("AlertTextColor"));
- }
- LLRect rect;
- rect.setLeftTopAndSize(msg_x, msg_y, text_rect.getWidth(),
- text_rect.getHeight());
- msg_box->setRect(rect);
- addChild(msg_box);
- // Buttons
- S32 button_left = (getRect().getWidth() - btn_total_width) / 2;
- for (S32 i = 0; i < num_options; ++i)
- {
- LLRect button_rect;
- button_rect.setOriginAndSize(button_left, DIALOG_VPAD, button_width,
- gBtnHeight);
- LLButton* btn = new LLButton(options[i].first, button_rect, "", "", "",
- NULL, NULL, mFont, options[i].second,
- options[i].second);
- mButtonData[i].mButton = btn;
- btn->setClickedCallback(&LLAlertDialog::onButtonPressed,
- (void*)(&mButtonData[i]));
- addChild(btn);
- if (i == mDefaultOption)
- {
- btn->setFocus(true);
- }
- button_left += button_width + BTN_HPAD;
- }
- // (Optional) Edit Box
- if (!edit_text_name.empty())
- {
- S32 y = (DIALOG_VPAD + DIALOG_VPAD / 2) + gBtnHeight;
- mLineEditor = new LLLineEditor(edit_text_name,
- LLRect(DIALOG_HPAD, y + EDITOR_HEIGHT,
- dialog_width - DIALOG_HPAD, y),
- edit_text_contents,
- LLFontGL::getFontSansSerif(),
- STD_STRING_STR_LEN);
- // Make sure all edit keys get handled properly (DEV-22396)
- mLineEditor->setHandleEditKeysDirectly(true);
- addChild(mLineEditor);
- }
- if (mLineEditor)
- {
- mLineEditor->setDrawAsterixes(is_password);
- setEditTextArgs(notification->getSubstitutions());
- }
- std::string ignore_label;
- LLNotificationForm::EIgnoreType form_type = form->getIgnoreType();
- if (form_type == LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE)
- {
- setCheckBox(gNotifications.getGlobalString("skipnexttime"),
- ignore_label);
- }
- else if (form_type == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
- {
- setCheckBox(gNotifications.getGlobalString("alwayschoose"),
- ignore_label);
- }
- }
- // All logic for deciding not to show an alert is done here, so that the alert
- // is valid until show() is called.
- bool LLAlertDialog::show()
- {
- // If this is a caution message, change the color and add an icon.
- setBackgroundColor(mCaution ? LLUI::sAlertCautionBoxColor
- : LLUI::sAlertBoxColor);
- startModal();
- gFloaterViewp->adjustToFitScreen(this);
- open();
- setFocus(true);
- if (mLineEditor)
- {
- mLineEditor->setFocus(true);
- mLineEditor->selectAll();
- }
- if (mDefaultOption >= 0)
- {
- // Delay before enabling default button
- mDefaultBtnTimer.start();
- mDefaultBtnTimer.setTimerExpirySec(DEFAULT_BUTTON_DELAY);
- }
- // Attach to floater if necessary
- LLUUID context_key = mNote->getPayload()["context"].asUUID();
- LLFloaterNotificationContext* contextp =
- dynamic_cast<LLFloaterNotificationContext*>(
- LLNotificationContext::getNamedInstance(context_key).get());
- if (contextp && contextp->getFloater())
- {
- contextp->getFloater()->addDependentFloater(this, false);
- }
- // Send the alert box data to the Lua automation script callback, when
- // any. HB
- if (sLuaCallback)
- {
- std::vector<std::string> buttons;
- for (size_t i = 0, count = mButtonData.size(); i < count; ++i)
- {
- LLButton* buttontp = mButtonData[i].mButton;
- if (buttontp)
- {
- buttons.emplace_back(buttontp->getCurrentLabel());
- }
- else if (!mButtonData[i].mURL.empty())
- {
- buttons.emplace_back(mButtonData[i].mURL);
- }
- else
- {
- buttons.emplace_back("<unset>");
- }
- }
- sLuaCallback(mNote->getName(), mNote->getID(), mNote->getMessage(),
- buttons);
- }
- return true;
- }
- bool LLAlertDialog::setCheckBox(const std::string& check_title,
- const std::string& check_control)
- {
- const S32 line_height = llfloor(mFont->getLineHeight() + 0.99f);
- // Extend dialog for "check next time"
- S32 max_msg_width = getRect().getWidth() - 2 * DIALOG_HPAD;
- S32 check_width = S32(mFont->getWidth(check_title) + 0.99f) + 16;
- max_msg_width = llmax(max_msg_width, check_width);
- S32 dialog_width = max_msg_width + 2 * DIALOG_HPAD;
- S32 dialog_height = getRect().getHeight();
- dialog_height += line_height + line_height / 2;
- reshape(dialog_width, dialog_height, false);
- S32 msg_x = (getRect().getWidth() - max_msg_width) / 2;
- LLRect check_rect;
- check_rect.setOriginAndSize(msg_x,
- DIALOG_VPAD + gBtnHeight + line_height / 2,
- max_msg_width, line_height);
- mCheck = new LLCheckBoxCtrl("check", check_rect, check_title, mFont,
- onClickIgnore, this);
- addChild(mCheck);
- return true;
- }
- void LLAlertDialog::setVisible(bool visible)
- {
- LLModalDialog::setVisible(visible);
- if (visible)
- {
- centerOnScreen();
- make_ui_sound("UISndAlert");
- }
- }
- void LLAlertDialog::onClose(bool app_quitting)
- {
- LLModalDialog::onClose(app_quitting);
- }
- bool LLAlertDialog::hasTitleBar() const
- {
- return isMinimizeable() || isCloseable() ||
- // Or if it has a title...
- (getCurrentTitle() != "" && getCurrentTitle() != " ");
- }
- //virtual
- bool LLAlertDialog::handleKeyHere(KEY key, MASK mask)
- {
- if (KEY_RETURN == key && mask == MASK_NONE)
- {
- LLModalDialog::handleKeyHere(key, mask);
- return true;
- }
- else if (KEY_RIGHT == key)
- {
- focusNextItem(false);
- return true;
- }
- else if (KEY_LEFT == key)
- {
- focusPrevItem(false);
- return true;
- }
- else if (KEY_TAB == key && mask == MASK_NONE)
- {
- focusNextItem(false);
- return true;
- }
- else if (KEY_TAB == key && mask == MASK_SHIFT)
- {
- focusPrevItem(false);
- return true;
- }
- else
- {
- return LLModalDialog::handleKeyHere(key, mask);
- }
- }
- //virtual
- void LLAlertDialog::draw()
- {
- // If the default button timer has just expired, activate the default
- // button
- if (mDefaultBtnTimer.hasExpired() && mDefaultBtnTimer.getStarted())
- {
- // prevent this block from being run more than once:
- mDefaultBtnTimer.stop();
- setDefaultBtn(mButtonData[mDefaultOption].mButton);
- }
- gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- LLUI::sColorDropShadow, LLUI::sDropShadowFloater);
- LLModalDialog::draw();
- }
- void LLAlertDialog::setEditTextArgs(const LLSD& edit_args)
- {
- if (mLineEditor)
- {
- std::string msg = mLineEditor->getText();
- mLineEditor->setText(msg);
- }
- else
- {
- llwarns << "Call done on dialog with no line editor" << llendl;
- }
- }
- bool LLAlertDialog::simulateClickButton(S32 i)
- {
- if (i < 0 || i >= (S32)mButtonData.size())
- {
- return false;
- }
- onButtonPressed(&mButtonData[i]);
- return true;
- }
- //static
- void LLAlertDialog::onButtonPressed(void* userdata)
- {
- ButtonData* button_data = (ButtonData*)userdata;
- LLAlertDialog* self = button_data->mSelf;
- if (!self || !button_data) return;
- LLSD response = self->mNote->getResponseTemplate();
- if (self->mLineEditor)
- {
- response[self->mLineEditor->getName()] = self->mLineEditor->getValue();
- }
- response[button_data->mButton->getName()] = true;
- // If we declared a URL and chose the URL option, go to the url
- if (!button_data->mURL.empty() && sURLLoader)
- {
- sURLLoader->load(button_data->mURL);
- }
- self->mNote->respond(response); // New notification reponse
- self->close(); // Delete self
- }
- //static
- void LLAlertDialog::onClickIgnore(LLUICtrl* ctrl, void* user_data)
- {
- LLAlertDialog* self = (LLAlertDialog*)user_data;
- if (!self) return;
- // Checkbox sometimes means "hide and do the default" and other times means
- // "warn me again". Yuck. JC
- bool check = ctrl->getValue();
- if (self->mNote->getForm()->getIgnoreType() ==
- LLNotificationForm::IGNORE_SHOW_AGAIN)
- {
- // Question was "show again" so invert value to get "ignore"
- check = !check;
- }
- self->mNote->setIgnored(check);
- }
|