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

Generated on Fri May 16 08:33:48 2008 for SecondLife by  doxygen 1.5.5