llnotify.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llnotify.h"
00035 
00036 #include "llchat.h"
00037 #include "llfocusmgr.h"
00038 
00039 #include "llbutton.h"
00040 #include "llfocusmgr.h"
00041 #include "llglheaders.h"
00042 #include "lliconctrl.h"
00043 #include "lltextbox.h"
00044 #include "lltexteditor.h"
00045 #include "lluiconstants.h"
00046 #include "llui.h"
00047 #include "llxmlnode.h"
00048 #include "llalertdialog.h"
00049 #include "llviewercontrol.h"
00050 #include "llviewerimagelist.h"
00051 #include "llfloaterchat.h"      // for add_chat_history()
00052 #include "lloverlaybar.h" // for gOverlayBar
00053 #include "lluictrlfactory.h"
00054 
00055 // Globals
00056 LLNotifyBoxView* gNotifyBoxView = NULL;
00057 
00058 const F32 ANIMATION_TIME = 0.333f;
00059 
00060 // statics
00061 S32 LLNotifyBox::sNotifyBoxCount = 0;
00062 const LLFontGL* LLNotifyBox::sFont = NULL;
00063 const LLFontGL* LLNotifyBox::sFontSmall = NULL;
00064 std::map<LLString, LLNotifyBox*> LLNotifyBox::sOpenUniqueNotifyBoxes;
00065 LLPointer<LLNotifyBoxTemplate> LLNotifyBox::sDefaultTemplate;
00066 
00067 
00068 LLNotifyBox::template_map_t LLNotifyBox::sNotifyTemplates;
00069 
00070 LLNotifyBox::LLNotifyBehavior::LLNotifyBehavior(notify_callback_t callback, void* data) :
00071         mCallback(callback),
00072         mData(data)
00073 {
00074 }
00075 
00076 //---------------------------------------------------------------------------
00077 // LLNotifyBox
00078 //---------------------------------------------------------------------------
00079 
00080 //static
00081 LLNotifyBox* LLNotifyBox::showXml( const LLString& xml_desc, notify_callback_t callback, void *user_data)
00082 {
00083         return showXml(xml_desc, LLString::format_map_t(), callback, user_data);
00084 }
00085 
00086 
00087 //static
00088 LLNotifyBox* LLNotifyBox::showXml( const LLString& xml_desc, const LLString::format_map_t& args, BOOL is_caution,
00089                                                    notify_callback_t callback, void *user_data)
00090 {
00091         // for script permission prompts
00092         LLPointer<LLNotifyBoxTemplate> xml_template = getTemplate(xml_desc);
00093         LLNotifyBox* notify = findExistingNotify(xml_template, args);
00094         if (notify)
00095         {
00096                 delete notify->mBehavior;
00097                 notify->mBehavior = new LLNotifyBehavior(callback, user_data);
00098         }
00099         else
00100         {
00101                 notify = new LLNotifyBox(xml_template, args, callback, user_data, is_caution);
00102                 gNotifyBoxView->addChildAtEnd(notify);
00103                 notify->moveToBack();
00104         }
00105         return notify;
00106 }
00107 
00108 //static
00109 LLNotifyBox* LLNotifyBox::showXml( const LLString& xml_desc, const LLString::format_map_t& args,
00110                                                    notify_callback_t callback, void *user_data)
00111 {
00112         LLPointer<LLNotifyBoxTemplate> xml_template = getTemplate(xml_desc);
00113         LLNotifyBox* notify = findExistingNotify(xml_template, args);
00114         if (notify)
00115         {
00116                 delete notify->mBehavior;
00117                 notify->mBehavior = new LLNotifyBehavior(callback, user_data);
00118         }
00119         else
00120         {
00121                 notify = new LLNotifyBox(xml_template, args, callback, user_data);
00122                 gNotifyBoxView->addChildAtEnd(notify);
00123                 notify->moveToBack();
00124         }
00125         return notify;
00126 }
00127 
00128 //static
00129 LLNotifyBox* LLNotifyBox::showXml( const LLString& xml_desc, const LLString::format_map_t& args,
00130                                                    notify_callback_t callback, void *user_data,
00131                                                    const option_list_t& options,
00132                                                    BOOL layout_script_dialog)
00133 {
00134         LLPointer<LLNotifyBoxTemplate> xml_template = getTemplate(xml_desc);
00135         LLNotifyBox* notify = findExistingNotify(xml_template, args);
00136         if (notify)
00137         {
00138                 delete notify->mBehavior;
00139                 notify->mBehavior = new LLNotifyBehavior(callback, user_data);
00140         }
00141         else
00142         {
00143                 notify = new LLNotifyBox(xml_template, args, callback, user_data, FALSE, options, layout_script_dialog);
00144                 gNotifyBoxView->addChild(notify);
00145         }
00146         return notify;
00147 }
00148 
00149 //static
00150 LLNotifyBox* LLNotifyBox::findExistingNotify(LLPointer<LLNotifyBoxTemplate> notify_template, const LLString::format_map_t &args)
00151 {
00152         if(notify_template->mUnique)
00153         {
00154                 LLString message = notify_template->mMessage;
00155                 LLAlertDialog::format(message, args);
00156                 unique_map_t::iterator found_it = sOpenUniqueNotifyBoxes.find(notify_template->mLabel + message);
00157                 if (found_it != sOpenUniqueNotifyBoxes.end())
00158                 {
00159                         return found_it->second;
00160                 }
00161         }
00162         return NULL;
00163 }
00164 
00165 void LLNotifyBox::cleanup()
00166 {
00167         sDefaultTemplate = NULL;
00168 }
00169 
00170 //---------------------------------------------------------------------------
00171 
00172 LLNotifyBox::LLNotifyBox(LLPointer<LLNotifyBoxTemplate> xml_template, const LLString::format_map_t& args,
00173                                                  notify_callback_t callback, void* user_data, BOOL is_caution,
00174                                                  const option_list_t& extra_options,
00175                                                  BOOL layout_script_dialog)
00176         : LLPanel(xml_template->mLabel, LLRect(), BORDER_NO),
00177           LLEventTimer(xml_template->mDuration),
00178           mIsTip(FALSE),
00179           mAnimating(TRUE),
00180           mUnique(xml_template->mUnique),
00181           mNextBtn(NULL),
00182           mBehavior(new LLNotifyBehavior(callback, user_data)),
00183           mNumOptions(0),
00184           mDefaultOption(0)
00185 {
00186         // clicking on a button does not steal current focus
00187         setIsChrome(TRUE);
00188 
00189         // class init
00190         if (!sFont)
00191         {
00192                 sFont = LLFontGL::sSansSerif;
00193                 sFontSmall = LLFontGL::sSansSerifSmall;
00194         }
00195 
00196         // setup paramaters
00197         
00198         mMessage = xml_template->mMessage;
00199         LLAlertDialog::format(mMessage, args);
00200 
00201         // use name + formatted text as unique key
00202         if (mUnique)
00203         {
00204                 sOpenUniqueNotifyBoxes[xml_template->mLabel + mMessage] = this;
00205         }
00206 
00207         option_list_t options = xml_template->mOptions;
00208         options.insert(options.end(), extra_options.begin(), extra_options.end());
00209 
00210         // initialize
00211 
00212         mIsTip = xml_template->mIsTip;
00213         mIsFocusRoot = !mIsTip;
00214 
00215         // caution flag can be set explicitly by specifying it in the
00216         // call to the c'tor, or it can be set implicitly if the
00217         // notify xml template specifies that it is a caution
00218         //
00219         // (but a tip-style notification cannot be a caution notification,
00220         // since the rendering of the additional top textbox doesn't 
00221         // account for the special layout of a tip notification)
00222         mIsCaution = ((xml_template->mIsCaution | is_caution) && (!mIsTip));
00223 
00224         // Don't animate if behind other windows
00225         if( gNotifyBoxView->getChildCount() > 0 )
00226                 mAnimating = FALSE;
00227         else
00228                 mAnimating = TRUE;
00229 
00230         mNumOptions = options.size();
00231         mDefaultOption = xml_template->mDefaultOption;
00232                   
00233         LLRect rect = mIsTip ? getNotifyTipRect(mMessage)
00234                                                  : getNotifyRect(mNumOptions, layout_script_dialog, mIsCaution);
00235         setRect(rect);
00236         setFollows(mIsTip ? (FOLLOWS_BOTTOM|FOLLOWS_RIGHT) : (FOLLOWS_TOP|FOLLOWS_RIGHT));
00237         setBackgroundVisible(FALSE);
00238         setBackgroundOpaque(TRUE);
00239 
00240         LLIconCtrl* icon;
00241         LLTextEditor* text;
00242 
00243         const S32 TOP = mRect.getHeight() - (mIsTip ? (S32)sFont->getLineHeight() : 32);
00244         const S32 BOTTOM = (S32)sFont->getLineHeight();
00245         S32 x = HPAD + HPAD;
00246         S32 y = TOP;
00247 
00248         if (mIsTip)
00249         {
00250                 // use the tip notification icon
00251                 icon = new LLIconCtrl("icon", LLRect(x, y, x+32, TOP-32), "notify_tip_icon.tga");
00252         }
00253         else if (mIsCaution)
00254         {
00255                 // use the caution notification icon
00256                 icon = new LLIconCtrl("icon", LLRect(x, y, x+32, TOP-32), "notify_caution_icon.tga");
00257         }
00258         else
00259         {
00260                 // use the default notification icon
00261                 icon = new LLIconCtrl("icon", LLRect(x, y, x+32, TOP-32), "notify_box_icon.tga");
00262         }
00263 
00264         icon->setMouseOpaque(FALSE);
00265         addChild(icon);
00266 
00267         x += HPAD + HPAD + 32;
00268 
00269         // add a caution textbox at the top of a caution notification
00270         LLTextBox* caution_box = NULL;
00271         if (mIsCaution)
00272         {
00273                 S32 caution_height = ((S32)sFont->getLineHeight() * 2) + VPAD;
00274                 caution_box = new LLTextBox(
00275                         "caution_box", 
00276                         LLRect(x, y, mRect.getWidth() - 2, caution_height), 
00277                         "", 
00278                         sFont, 
00279                         FALSE);
00280 
00281                 caution_box->setFontStyle(LLFontGL::BOLD);
00282                 caution_box->setColor(gColors.getColor("NotifyCautionWarnColor"));
00283                 caution_box->setBackgroundColor(gColors.getColor("NotifyCautionBoxColor"));
00284                 caution_box->setBorderVisible(FALSE);
00285                 caution_box->setWrappedText(LLNotifyBox::getTemplateMessage("ScriptQuestionCautionWarn"));
00286                 
00287                 addChild(caution_box);
00288 
00289                 // adjust the vertical position of the next control so that 
00290                 // it appears below the caution textbox
00291                 y = y - caution_height;
00292         }
00293 
00294         const S32 BOTTOM_PAD = VPAD * 3;
00295         const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD);
00296 
00297         // Tokenization on \n is handled by LLTextBox
00298 
00299         const S32 MAX_LENGTH = 512 + 20 + 
00300                 DB_FIRST_NAME_BUF_SIZE + 
00301                 DB_LAST_NAME_BUF_SIZE +
00302                 DB_INV_ITEM_NAME_BUF_SIZE;  // For script dialogs: add space for title.
00303 
00304         text = new LLTextEditor("box",
00305                                                         LLRect(x, y, mRect.getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16),
00306                                                         MAX_LENGTH,
00307                                                         mMessage,
00308                                                         sFont,
00309                                                         FALSE);
00310         text->setWordWrap(TRUE);
00311         text->setTakesFocus(FALSE);
00312         text->setMouseOpaque(FALSE);
00313         text->setBorderVisible(FALSE);
00314         text->setTakesNonScrollClicks(FALSE);
00315         text->setHideScrollbarForShortDocs(TRUE);
00316         text->setReadOnlyBgColor ( LLColor4::transparent ); // the background color of the box is manually 
00317                                                             // rendered under the text box, therefore we want 
00318                                                                                                                 // the actual text box to be transparent
00319         text->setReadOnlyFgColor ( gColors.getColor("NotifyTextColor") );
00320         text->setEnabled(FALSE); // makes it read-only
00321         text->setTabStop(FALSE); // can't tab to it (may be a problem for scrolling via keyboard)
00322         addChild(text);
00323 
00324         if (mIsTip)
00325         {
00326                 // TODO: Make a separate archive for these.
00327                 LLChat chat(mMessage);
00328                 chat.mSourceType = CHAT_SOURCE_SYSTEM;
00329                 LLFloaterChat::getInstance(LLSD())->addChatHistory(chat);
00330         }
00331         else
00332         {
00333                 LLButton* btn;
00334                 btn = new LLButton("next",
00335                                                    LLRect(mRect.getWidth()-18, BOTTOM_PAD+16, mRect.getWidth()-2, BOTTOM_PAD+2),
00336                                                    "notify_next.tga",
00337                                                    "notify_next.tga",
00338                                                    "",
00339                                                    onClickNext,
00340                                                    this,
00341                                                    sFont);
00342                 btn->setToolTip("Next");
00343                 addChild(btn);
00344                 mNextBtn = btn;
00345 
00346                 // make caution notification buttons slightly narrower
00347                 // so that 3 of them can fit without overlapping the "next" button
00348                 S32 btn_width = mIsCaution? 84 : 90;
00349                 LLRect btn_rect;
00350 
00351                 for (S32 i = 0; i < mNumOptions; i++)
00352                 {
00353                         S32 index = i;
00354                         S32 btn_height= BTN_HEIGHT;
00355                         const LLFontGL* font = sFont;
00356                         S32 ignore_pad = 0;
00357 
00358                         if (layout_script_dialog)
00359                         {
00360                                 // Add two "blank" option spaces, before the "Ignore" button
00361                                 index = i + 2;
00362                                 if (i == 0)
00363                                 {
00364                                         // Ignore button is smaller, less wide
00365                                         btn_height = BTN_HEIGHT_SMALL;
00366                                         font = sFontSmall;
00367                                         ignore_pad = 10;
00368                                 }
00369                         }
00370 
00371                         btn_rect.setOriginAndSize(x + (index % 3) * (btn_width+HPAD+HPAD) + ignore_pad,
00372                                                                           BOTTOM_PAD + (index / 3) * (BTN_HEIGHT+VPAD),
00373                                                                           btn_width - 2*ignore_pad,
00374                                                                           btn_height);
00375 
00376                         InstanceAndS32* userdata = new InstanceAndS32;
00377                         userdata->mSelf = this;
00378                         userdata->mButton = i;
00379 
00380                         mBtnCallbackData.push_back(userdata);
00381 
00382                         btn = new LLButton(options[i], btn_rect, "", onClickButton, userdata);
00383                         btn->setFont(font);
00384 
00385                         if (mIsCaution)
00386                         {
00387                                 btn->setImageColor(LLUI::sColorsGroup->getColor("ButtonCautionImageColor"));
00388                                 btn->setDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonCautionImageColor"));
00389                         }
00390 
00391                         addChild(btn, -1);
00392 
00393                         if (i == mDefaultOption)
00394                         {
00395                                 setDefaultBtn(btn);
00396                         }
00397                 }
00398                 
00399                 sNotifyBoxCount++;
00400 
00401                 // If this is the only notify box, don't show the next button
00402                 if (sNotifyBoxCount == 1
00403                         && mNextBtn)
00404                 {
00405                         mNextBtn->setVisible(FALSE);
00406                 }
00407         }
00408 }
00409 
00410 // virtual
00411 LLNotifyBox::~LLNotifyBox()
00412 {
00413         delete mBehavior;
00414         mBehavior = NULL;
00415 
00416         std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer());
00417 
00418         if (mUnique)
00419         {
00420                 sOpenUniqueNotifyBoxes.erase(mName + mMessage);
00421         }
00422 }
00423 
00424 // virtual
00425 BOOL LLNotifyBox::handleMouseUp(S32 x, S32 y, MASK mask)
00426 {
00427         if (mIsTip)
00428         {
00429                 close();
00430                 return TRUE;
00431         }
00432 
00433         setFocus(TRUE);
00434 
00435         return LLPanel::handleMouseUp(x, y, mask);
00436 }
00437 
00438 // virtual
00439 BOOL LLNotifyBox::handleRightMouseDown(S32 x, S32 y, MASK mask)
00440 {
00441         if (!mIsTip && getVisible() && getEnabled() && pointInView(x,y))
00442         {
00443                 moveToBack();
00444                 return TRUE;
00445         }
00446 
00447         return LLPanel::handleRightMouseDown(x, y, mask);
00448 }
00449 
00450 
00451 // virtual
00452 void LLNotifyBox::draw()
00453 {
00454         F32 display_time = mAnimateTimer.getElapsedTimeF32();
00455 
00456         if (mAnimating && display_time < ANIMATION_TIME)
00457         {
00458                 glMatrixMode(GL_MODELVIEW);
00459                 LLUI::pushMatrix();
00460 
00461                 S32 height = mRect.getHeight();
00462                 F32 fraction = display_time / ANIMATION_TIME;
00463                 F32 voffset = (1.f - fraction) * height;
00464                 if (mIsTip) voffset *= -1.f;
00465                 LLUI::translate(0.f, voffset, 0.f);
00466                 
00467                 drawBackground();
00468 
00469                 LLPanel::draw();
00470 
00471                 LLUI::popMatrix();
00472         }
00473         else
00474         {
00475                 if(mAnimating)
00476                 {
00477                         mAnimating = FALSE;
00478                         if(!mIsTip) 
00479                         {
00480                                 // hide everyone behind me once I'm done animating
00481                                 gNotifyBoxView->showOnly(this);
00482                         }
00483                 }
00484                 drawBackground();
00485                 LLPanel::draw();
00486         }
00487 }
00488 
00489 void LLNotifyBox::drawBackground() const
00490 {
00491         LLUUID image_id;
00492         image_id.set(gViewerArt.getString("rounded_square.tga"));
00493         LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
00494         if (imagep)
00495         {
00496                 LLGLSTexture texture_enabled;
00497                 LLViewerImage::bindTexture(imagep);
00498                 // set proper background color depending on whether notify box is a caution or not
00499                 LLColor4 color = mIsCaution? gColors.getColor("NotifyCautionBoxColor") : gColors.getColor("NotifyBoxColor");
00500                 if(gFocusMgr.childHasKeyboardFocus( this ))
00501                 {
00502                         const S32 focus_width = 2;
00503                         color = gColors.getColor("FloaterFocusBorderColor");
00504                         glColor4fv(color.mV);
00505                         gl_segmented_rect_2d_tex(-focus_width, mRect.getHeight() + focus_width, 
00506                                                                         mRect.getWidth() + focus_width, -focus_width,
00507                                                                         imagep->getWidth(), imagep->getHeight(),
00508                                                                         16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM);
00509                         color = gColors.getColor("ColorDropShadow");
00510                         glColor4fv(color.mV);
00511                         gl_segmented_rect_2d_tex(0, mRect.getHeight(), mRect.getWidth(), 0, imagep->getWidth(), imagep->getHeight(), 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM);
00512 
00513                         if( mIsCaution )
00514                                 color = gColors.getColor("NotifyCautionBoxColor");
00515                         else
00516                                 color = gColors.getColor("NotifyBoxColor");
00517 
00518                         glColor4fv(color.mV);
00519                         gl_segmented_rect_2d_tex(1, mRect.getHeight()-1, mRect.getWidth()-1, 1, imagep->getWidth(), imagep->getHeight(), 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM);
00520                 }
00521                 else
00522                 {
00523                         glColor4fv(color.mV);
00524                         gl_segmented_rect_2d_tex(0, mRect.getHeight(), mRect.getWidth(), 0, imagep->getWidth(), imagep->getHeight(), 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM);
00525                 }
00526         }
00527 }
00528 
00529 
00530 void LLNotifyBox::close()
00531 {
00532         BOOL isTipTmp = mIsTip;
00533 
00534         if (!mIsTip)
00535         {
00536                 sNotifyBoxCount--;
00537         }
00538 
00539         die();
00540         if(!isTipTmp)
00541         {
00542                 LLNotifyBox * front = gNotifyBoxView->getFirstNontipBox();
00543                 if(front)
00544                 {
00545                         gNotifyBoxView->showOnly(front);
00546                         // we're assuming that close is only called by user action (for non-tips),
00547                         // so we then give focus to the next close button
00548                         if (front->mDefaultBtn)
00549                         {
00550                                 front->mDefaultBtn->setFocus(TRUE);
00551                         }
00552                         gFocusMgr.triggerFocusFlash(); // TODO it's ugly to call this here
00553                 }
00554         }
00555 }
00556 
00557 /*virtual*/
00558 BOOL LLNotifyBox::tick()
00559 {
00560         if (mIsTip)
00561         {
00562                 close();
00563         }
00564         return FALSE;
00565 }
00566 
00567 void LLNotifyBox::setVisible(BOOL visible)
00568 {
00569         // properly set the status of the next button
00570         if(visible && !mIsTip)
00571         {
00572                 mNextBtn->setVisible(sNotifyBoxCount > 1);
00573                 mNextBtn->setEnabled(sNotifyBoxCount > 1);
00574         }
00575         LLPanel::setVisible(visible);
00576 }
00577 
00578 void LLNotifyBox::moveToBack()
00579 {
00580         // Move this dialog to the back.
00581         gNotifyBoxView->sendChildToBack(this);
00582         if(!mIsTip && mNextBtn)
00583         {
00584                 mNextBtn->setVisible(FALSE);
00585 
00586                 // And enable the next button on the frontmost one, if there is one
00587                 if (gNotifyBoxView->getChildCount() > 0)
00588                 {
00589                         LLNotifyBox* front = gNotifyBoxView->getFirstNontipBox();
00590                         if (front)
00591                         {
00592                                 gNotifyBoxView->showOnly(front);
00593                                 // assuming that moveToBack is only called by clicking the next button,
00594                                 // we give focus to the next next button
00595                                 front->mNextBtn->setFocus(TRUE);
00596                                 gFocusMgr.triggerFocusFlash(); // TODO: it's ugly to call this here
00597                         }
00598                 }
00599         }
00600 }
00601 
00602 
00603 // static
00604 LLRect LLNotifyBox::getNotifyRect(S32 num_options, BOOL layout_script_dialog, BOOL is_caution)
00605 {
00606         S32 notify_height = gSavedSettings.getS32("NotifyBoxHeight");
00607         if (is_caution)
00608         {
00609                 // make caution-style dialog taller to accomodate extra text,
00610                 // as well as causing the accept/decline buttons to be drawn
00611                 // in a different position, to help prevent "quick-click-through"
00612                 // of many permissions prompts
00613                 notify_height = gSavedSettings.getS32("PermissionsCautionNotifyBoxHeight");
00614         }
00615         const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth");
00616 
00617         const S32 TOP = gNotifyBoxView->getRect().getHeight();
00618         const S32 RIGHT = gNotifyBoxView->getRect().getWidth();
00619         const S32 LEFT = RIGHT - NOTIFY_WIDTH;
00620 
00621         if (num_options < 1)
00622         {
00623                 num_options = 1;
00624         }
00625 
00626         // Add two "blank" option spaces.
00627         if (layout_script_dialog)
00628         {
00629                 num_options += 2;
00630         }
00631 
00632         S32 additional_lines = (num_options-1) / 3;
00633 
00634         notify_height += additional_lines * (BTN_HEIGHT + VPAD);
00635 
00636         return LLRect(LEFT, TOP, RIGHT, TOP-notify_height);
00637 }
00638 
00639 // static
00640 LLRect LLNotifyBox::getNotifyTipRect(const LLString &utf8message)
00641 {
00642         S32 line_count = 1;
00643         LLWString message = utf8str_to_wstring(utf8message);
00644         S32 message_len = message.length();
00645 
00646         const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth");
00647         // Make room for the icon area.
00648         const S32 text_area_width = NOTIFY_WIDTH - HPAD * 4 - 32;
00649 
00650         const llwchar* wchars = message.c_str();
00651         const llwchar* start = wchars;
00652         const llwchar* end;
00653         S32 total_drawn = 0;
00654         BOOL done = FALSE;
00655 
00656         do
00657         {
00658                 line_count++;
00659 
00660                 for (end=start; *end != 0 && *end != '\n'; end++)
00661                         ;
00662 
00663                 if( *end == 0 )
00664                 {
00665                         end = wchars + message_len;
00666                         done = TRUE;
00667                 }
00668                 
00669                 S32 remaining = end - start;
00670                 while( remaining )
00671                 {
00672                         S32 drawn = sFont->maxDrawableChars( start, (F32)text_area_width, remaining, TRUE );
00673 
00674                         if( 0 == drawn )
00675                         {
00676                                 drawn = 1;  // Draw at least one character, even if it doesn't all fit. (avoids an infinite loop)
00677                         }
00678 
00679                         total_drawn += drawn; 
00680                         start += drawn;
00681                         remaining -= drawn;
00682                         
00683                         if( total_drawn < message_len )
00684                         {
00685                                 if( (wchars[ total_drawn ] != '\n') )
00686                                 {
00687                                         // wrap because line was too long
00688                                         line_count++;
00689                                 }
00690                         }
00691                         else
00692                         {
00693                                 done = TRUE;
00694                         }
00695                 }
00696 
00697                 total_drawn++;  // for '\n'
00698                 end++;
00699                 start = end;
00700         } while( !done );
00701 
00702         const S32 MIN_NOTIFY_HEIGHT = 72;
00703         const S32 MAX_NOTIFY_HEIGHT = 600;
00704         S32 notify_height = llceil((F32) (line_count+1) * sFont->getLineHeight());
00705         if(gOverlayBar)
00706         {
00707                 notify_height += gOverlayBar->getRect().getHeight();
00708         }
00709         else
00710         {
00711                 // *FIX: this is derived from the padding caused by the
00712                 // rounded rects, shouldn't be a const here.
00713                 notify_height += 10;  
00714         }
00715         notify_height += VPAD;
00716         notify_height = llclamp(notify_height, MIN_NOTIFY_HEIGHT, MAX_NOTIFY_HEIGHT);
00717 
00718         const S32 RIGHT = gNotifyBoxView->getRect().getWidth();
00719         const S32 LEFT = RIGHT - NOTIFY_WIDTH;
00720 
00721         // Make sure it goes slightly offscreen
00722         return LLRect(LEFT, notify_height-1, RIGHT, -1);
00723 }
00724 
00725 
00726 // static
00727 void LLNotifyBox::onClickButton(void* data)
00728 {
00729         InstanceAndS32* self_and_button = (InstanceAndS32*)data;
00730         LLNotifyBox* self = self_and_button->mSelf;
00731         S32 button = self_and_button->mButton;
00732 
00733         // for caution notifications, check if the last button in the prompt was clicked
00734         // unless it is the only button, in which case it will just be an "OK" button
00735         if ((self->mIsCaution) && (button > 0) && (button == (self->mNumOptions - 1)))
00736         {
00737                 // show an alert dialog containing more explanation about the debit permission
00738                 LLAlertDialog::showXml("DebitPermissionDetails");
00739 
00740                 // keep this notification open
00741                 return;
00742         }
00743 
00744         if (self->mBehavior->mCallback)
00745         {
00746                 self->mBehavior->mCallback(button, self->mBehavior->mData);
00747                 self->mBehavior->mCallback = NULL; // Notification callbacks only expect to be called once ever
00748         }
00749 
00750         self->close();
00751 }
00752 
00753 
00754 // static
00755 void LLNotifyBox::onClickNext(void* data)
00756 {
00757         LLNotifyBox* self = static_cast<LLNotifyBox*>(data);
00758         self->moveToBack();
00759 }
00760 
00761 // static
00762 LLPointer<LLNotifyBoxTemplate> LLNotifyBox::getTemplate(const LLString& xml_desc)
00763 {
00764         // get template
00765         
00766         if (!sDefaultTemplate)
00767         {
00768                 // default template is non-unique, of course
00769                 sDefaultTemplate = new LLNotifyBoxTemplate(FALSE, gSavedSettings.getF32("NotifyTipDuration"));
00770                 sDefaultTemplate->addOption("OK", FALSE);
00771         }
00772         
00773         LLPointer<LLNotifyBoxTemplate> xml_template;
00774         template_map_t::iterator iter = sNotifyTemplates.find(xml_desc);
00775         if (iter != sNotifyTemplates.end())
00776         {
00777                 xml_template = iter->second;
00778         }
00779         else
00780         {
00781                 LLString tmsg = "[Notification template not found:\n " + xml_desc + " ]";
00782                 sDefaultTemplate->setMessage(tmsg);
00783                 xml_template = sDefaultTemplate;
00784         }
00785 
00786         return xml_template;
00787 }
00788 
00789 //-----------------------------------------------------------------------------
00790 
00791 //static
00792 const LLString LLNotifyBox::getTemplateMessage(const LLString& xml_desc, const LLString::format_map_t& args)
00793 {
00794         template_map_t::iterator iter = sNotifyTemplates.find(xml_desc);
00795         if (iter != sNotifyTemplates.end())
00796         {
00797                 LLString message = iter->second->mMessage;
00798                 LLAlertDialog::format(message, args);
00799                 return message;
00800         }
00801         else
00802         {
00803                 return xml_desc;
00804         }
00805 }
00806 
00807 //static
00808 const LLString LLNotifyBox::getTemplateMessage(const LLString& xml_desc)
00809 {
00810         template_map_t::iterator iter = sNotifyTemplates.find(xml_desc);
00811         if (iter != sNotifyTemplates.end())
00812         {
00813                 return iter->second->mMessage;
00814         }
00815         else
00816         {
00817                 return xml_desc;
00818         }
00819 }
00820 
00821 // method to check whether a given notify template show as a caution or not
00822 BOOL LLNotifyBox::getTemplateIsCaution(const LLString& xml_desc)
00823 {
00824         BOOL is_caution = FALSE;
00825 
00826         template_map_t::iterator iter = sNotifyTemplates.find(xml_desc);
00827         if (iter != sNotifyTemplates.end())
00828         {
00829                 is_caution = iter->second->mIsCaution;
00830         }
00831 
00832         return is_caution;
00833 }
00834 
00835 //static
00836 bool LLNotifyBox::parseNotify(const LLString& xml_filename)
00837 {
00838         LLXMLNodePtr root;
00839 
00840         BOOL success  = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
00841 
00842         if (!success || root.isNull() || !root->hasName( "notifications" ))
00843         {
00844                 llerrs << "Problem reading UI Notify file: " << xml_filename << llendl;
00845                 return false;
00846         }
00847 
00848         for (LLXMLNode* notify = root->getFirstChild();
00849                  notify != NULL; notify = notify->getNextSibling())
00850         {
00851                 if (!notify->hasName("notify"))
00852                 {
00853                         continue;
00854                 }
00855 
00856                 BOOL unique = FALSE;
00857                 notify->getAttributeBOOL("unique", unique);
00858 
00859                 F32 duration = gSavedSettings.getF32("NotifyTipDuration");
00860                 notify->getAttributeF32("duration", duration);
00861                 
00862                 LLPointer<LLNotifyBoxTemplate> xml_template = new LLNotifyBoxTemplate(unique, duration);
00863 
00864                 // label=
00865                 LLString notify_name;
00866                 if (notify->getAttributeString("name", notify_name))
00867                 {
00868                         xml_template->mLabel = notify_name;
00869                 }
00870                 else
00871                 {
00872                         llwarns << "Unable to parse notify with no name" << llendl;
00873                         continue;
00874                 }
00875                 // modal=
00876                 BOOL tip;
00877                 if (notify->getAttributeBOOL("tip", tip))
00878                 {
00879                         xml_template->mIsTip = tip;
00880                 }
00881 
00882                 // parse a bool attribute named "caution" to determine
00883                 // whether this notification gets cautionary special handling
00884                 BOOL caution = FALSE;
00885                 if (notify->getAttributeBOOL("caution", caution))
00886                 {
00887                         if (xml_template)
00888                         {
00889                                 xml_template->mIsCaution = caution;
00890                         }
00891                 }
00892                 
00893                                 
00894                 S32 btn_idx = 0;
00895                 for (LLXMLNode* child = notify->getFirstChild();
00896                          child != NULL; child = child->getNextSibling())
00897                 {
00898                         // <message>
00899                         if (child->hasName("message"))
00900                         {
00901                                 xml_template->mMessage = child->getTextContents();
00902                         }
00903 
00904                         // <option>
00905                         if (child->hasName("option"))
00906                         {
00907                                 LLString label = child->getValue();
00908                                 BOOL is_default = FALSE;
00909                                 child->getAttributeBOOL("default", is_default);
00910                                 LLString ignore_text;
00911                                 if (!child->getAttributeString("ignore", ignore_text))
00912                                 {
00913                                         ignore_text = label;
00914                                 }
00915                                 xml_template->addOption(label, is_default);
00916                                 btn_idx++;
00917                         }
00918                 }
00919 
00920                 //*TODO:translate
00921                 if (xml_template->mOptions.empty())
00922                 {
00923                         xml_template->addOption("OK", FALSE);
00924                 }
00925                 sNotifyTemplates[xml_template->mLabel] = xml_template;
00926         }
00927         return true;
00928 }
00929 
00930 LLNotifyBoxView::LLNotifyBoxView(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows)
00931         : LLUICtrl(name,rect,mouse_opaque,NULL,NULL,follows) 
00932 {
00933 }
00934 
00935 LLNotifyBox * LLNotifyBoxView::getFirstNontipBox() const
00936 {
00937         // *TODO: Don't make assumptions like this!
00938         // assumes every child is a notify box
00939         for(child_list_const_iter_t iter = getChildList()->begin();
00940                         iter != getChildList()->end();
00941                         iter++)
00942         {
00943                 // hack! *TODO: Integrate llnotify and llgroupnotify
00944                 LLView* view = *iter;
00945                 if (view->getName() == "groupnotify")
00946                         continue;
00947                 LLNotifyBox* box = static_cast<LLNotifyBox*>(view);
00948                 if(!box->isTip() && !box->isDead())
00949                 {
00950                         return box;
00951                 }
00952         }
00953         return NULL;
00954 }
00955 
00956 void LLNotifyBoxView::showOnly(LLView * view)
00957 {
00958         if(view) 
00959         {
00960                 // assumes that the argument is actually a child
00961                 LLNotifyBox * shown = static_cast<LLNotifyBox*>(view);
00962                 // make every other notification invisible
00963                 for(child_list_const_iter_t iter = getChildList()->begin();
00964                         iter != getChildList()->end();
00965                         iter++)
00966                 {
00967                         LLNotifyBox * box = static_cast<LLNotifyBox*>(*iter);
00968                         if(box != view && box->getVisible() && !box->isTip())
00969                         {
00970                                 box->setVisible(FALSE);
00971                         }
00972                 }
00973                 shown->setVisible(TRUE);
00974                 sendChildToFront(shown);
00975         }
00976 }

Generated on Thu Jul 1 06:08:55 2010 for Second Life Viewer by  doxygen 1.4.7