diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llnotify.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/llnotify.cpp | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/linden/indra/newview/llnotify.cpp b/linden/indra/newview/llnotify.cpp new file mode 100644 index 0000000..c7d3a95 --- /dev/null +++ b/linden/indra/newview/llnotify.cpp | |||
@@ -0,0 +1,745 @@ | |||
1 | /** | ||
2 | * @file llnotify.cpp | ||
3 | * @brief Non-blocking notification that doesn't take keyboard focus. | ||
4 | * | ||
5 | * Copyright (c) 2003-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | #include "llnotify.h" | ||
31 | |||
32 | #include "llchat.h" | ||
33 | #include "llfocusmgr.h" | ||
34 | |||
35 | #include "llbutton.h" | ||
36 | #include "llfocusmgr.h" | ||
37 | #include "llglheaders.h" | ||
38 | #include "lliconctrl.h" | ||
39 | #include "lltextbox.h" | ||
40 | #include "lltexteditor.h" | ||
41 | #include "lluiconstants.h" | ||
42 | #include "llui.h" | ||
43 | #include "llxmlnode.h" | ||
44 | #include "llalertdialog.h" | ||
45 | #include "llviewercontrol.h" | ||
46 | #include "llviewerimagelist.h" | ||
47 | #include "llfloaterchat.h" // for add_chat_history() | ||
48 | #include "lloverlaybar.h" // for gOverlayBar | ||
49 | #include "lluictrlfactory.h" | ||
50 | |||
51 | // Globals | ||
52 | LLNotifyBoxView* gNotifyBoxView = NULL; | ||
53 | |||
54 | const F32 ANIMATION_TIME = 0.333f; | ||
55 | |||
56 | // statics | ||
57 | S32 LLNotifyBox::sNotifyBoxCount = 0; | ||
58 | const LLFontGL* LLNotifyBox::sFont = NULL; | ||
59 | const LLFontGL* LLNotifyBox::sFontSmall = NULL; | ||
60 | |||
61 | LLNotifyBox::template_map_t LLNotifyBox::sNotifyTemplates; | ||
62 | |||
63 | //--------------------------------------------------------------------------- | ||
64 | // LLNotifyBox | ||
65 | //--------------------------------------------------------------------------- | ||
66 | |||
67 | //static | ||
68 | void LLNotifyBox::showXml( const LLString& xml_desc, notify_callback_t callback, void *user_data) | ||
69 | { | ||
70 | return showXml(xml_desc, LLString::format_map_t(), callback, user_data); | ||
71 | } | ||
72 | |||
73 | //static | ||
74 | void LLNotifyBox::showXml( const LLString& xml_desc, const LLString::format_map_t& args, | ||
75 | notify_callback_t callback, void *user_data) | ||
76 | { | ||
77 | LLNotifyBox* notify = new LLNotifyBox(xml_desc, args, callback, user_data); | ||
78 | gNotifyBoxView->addChild(notify); | ||
79 | } | ||
80 | |||
81 | //static | ||
82 | void LLNotifyBox::showXml( const LLString& xml_desc, const LLString::format_map_t& args, | ||
83 | notify_callback_t callback, void *user_data, | ||
84 | const option_list_t& options, | ||
85 | BOOL layout_script_dialog) | ||
86 | { | ||
87 | LLNotifyBox* notify = new LLNotifyBox(xml_desc, args, callback, user_data, options, layout_script_dialog); | ||
88 | gNotifyBoxView->addChild(notify); | ||
89 | } | ||
90 | |||
91 | //--------------------------------------------------------------------------- | ||
92 | |||
93 | LLNotifyBox::LLNotifyBox(const LLString& xml_desc, const LLString::format_map_t& args, | ||
94 | notify_callback_t callback, void* user_data, | ||
95 | const option_list_t& extra_options, | ||
96 | BOOL layout_script_dialog) | ||
97 | : LLPanel("notify", LLRect(), BORDER_NO), | ||
98 | LLEventTimer(gSavedSettings.getF32("NotifyTipDuration")), | ||
99 | mIsTip(FALSE), | ||
100 | mAnimating(TRUE), | ||
101 | mTimer(), | ||
102 | mNextBtn(NULL), | ||
103 | mCallback(callback), | ||
104 | mData(user_data), | ||
105 | mNumOptions(0), | ||
106 | mDefaultOption(0) | ||
107 | { | ||
108 | // clicking on a button does not steal current focus | ||
109 | setIsChrome(TRUE); | ||
110 | |||
111 | // class init | ||
112 | if (!sFont) | ||
113 | { | ||
114 | sFont = LLFontGL::sSansSerif; | ||
115 | sFontSmall = LLFontGL::sSansSerifSmall; | ||
116 | } | ||
117 | |||
118 | // get template | ||
119 | |||
120 | static LLNotifyBoxTemplate default_template; | ||
121 | LLNotifyBoxTemplate* xml_template; | ||
122 | template_map_t::iterator iter = sNotifyTemplates.find(xml_desc); | ||
123 | if (iter != sNotifyTemplates.end()) | ||
124 | { | ||
125 | xml_template = iter->second; | ||
126 | } | ||
127 | else | ||
128 | { | ||
129 | LLString tmsg = "[Notification template not found:\n " + xml_desc + " ]"; | ||
130 | default_template.setMessage(tmsg); | ||
131 | xml_template = &default_template; | ||
132 | } | ||
133 | |||
134 | // setup paramaters | ||
135 | |||
136 | LLString message = xml_template->mMessage; | ||
137 | LLAlertDialog::format(message, args); | ||
138 | |||
139 | option_list_t options = xml_template->mOptions; | ||
140 | options.insert(options.end(), extra_options.begin(), extra_options.end()); | ||
141 | |||
142 | // initialize | ||
143 | |||
144 | mIsTip = xml_template->mIsTip; | ||
145 | mIsFocusRoot = !mIsTip; | ||
146 | mAnimating = TRUE; | ||
147 | mCallback = callback; | ||
148 | mData = user_data; | ||
149 | mNumOptions = options.size(); | ||
150 | mDefaultOption = xml_template->mDefaultOption; | ||
151 | |||
152 | LLRect rect = mIsTip ? getNotifyTipRect(message) | ||
153 | : getNotifyRect(mNumOptions, layout_script_dialog); | ||
154 | setRect(rect); | ||
155 | setFollows(mIsTip ? (FOLLOWS_BOTTOM|FOLLOWS_RIGHT) : (FOLLOWS_TOP|FOLLOWS_RIGHT)); | ||
156 | setBackgroundVisible(FALSE); | ||
157 | setBackgroundOpaque(TRUE); | ||
158 | |||
159 | LLIconCtrl* icon; | ||
160 | LLTextEditor* text; | ||
161 | |||
162 | S32 x = HPAD + HPAD; | ||
163 | const S32 TOP = mRect.getHeight() - (mIsTip ? (S32)sFont->getLineHeight() : 32); | ||
164 | const S32 BOTTOM = (S32)sFont->getLineHeight(); | ||
165 | |||
166 | icon = new LLIconCtrl("icon", | ||
167 | LLRect(x, TOP, x+32, TOP-32), | ||
168 | mIsTip ? "notify_tip_icon.tga" : "notify_box_icon.tga"); | ||
169 | icon->setMouseOpaque(FALSE); | ||
170 | addChild(icon); | ||
171 | |||
172 | x += HPAD + HPAD + 32; | ||
173 | |||
174 | const S32 BOTTOM_PAD = VPAD * 3; | ||
175 | const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD); | ||
176 | |||
177 | // Tokenization on \n is handled by LLTextBox | ||
178 | |||
179 | const S32 MAX_LENGTH = 512 + 20 + | ||
180 | DB_FIRST_NAME_BUF_SIZE + | ||
181 | DB_LAST_NAME_BUF_SIZE + | ||
182 | DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title. | ||
183 | |||
184 | text = new LLTextEditor("box", | ||
185 | LLRect(x, TOP, mRect.getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), | ||
186 | MAX_LENGTH, | ||
187 | message, | ||
188 | sFont, | ||
189 | FALSE); | ||
190 | text->setWordWrap(TRUE); | ||
191 | text->setTakesFocus(FALSE); | ||
192 | text->setMouseOpaque(FALSE); | ||
193 | text->setBorderVisible(FALSE); | ||
194 | text->setTakesNonScrollClicks(FALSE); | ||
195 | text->setHideScrollbarForShortDocs(TRUE); | ||
196 | text->setReadOnlyBgColor ( LLColor4::transparent ); // the background color of the box is manually | ||
197 | // rendered under the text box, therefore we want | ||
198 | // the actual text box to be transparent | ||
199 | text->setReadOnlyFgColor ( gColors.getColor("NotifyTextColor") ); | ||
200 | text->setEnabled(FALSE); // makes it read-only | ||
201 | text->setTabStop(FALSE); // can't tab to it (may be a problem for scrolling via keyboard) | ||
202 | addChild(text); | ||
203 | |||
204 | if (mIsTip) | ||
205 | { | ||
206 | // TODO: Make a separate archive for these. | ||
207 | LLChat chat(message); | ||
208 | chat.mSourceType = CHAT_SOURCE_SYSTEM; | ||
209 | gFloaterChat->addChatHistory(chat); | ||
210 | } | ||
211 | else | ||
212 | { | ||
213 | LLButton* btn; | ||
214 | btn = new LLButton("next", | ||
215 | LLRect(mRect.getWidth()-24, BOTTOM_PAD+16, mRect.getWidth()-8, BOTTOM_PAD), | ||
216 | "notify_next.tga", | ||
217 | "notify_next.tga", | ||
218 | "", | ||
219 | onClickNext, | ||
220 | this, | ||
221 | sFont); | ||
222 | btn->setToolTip("Next"); | ||
223 | addChild(btn); | ||
224 | mNextBtn = btn; | ||
225 | |||
226 | S32 btn_width = 90; | ||
227 | LLRect btn_rect; | ||
228 | |||
229 | for (S32 i = 0; i < mNumOptions; i++) | ||
230 | { | ||
231 | S32 index = i; | ||
232 | S32 btn_height= BTN_HEIGHT; | ||
233 | const LLFontGL* font = sFont; | ||
234 | S32 ignore_pad = 0; | ||
235 | |||
236 | if (layout_script_dialog) | ||
237 | { | ||
238 | // Add two "blank" option spaces, before the "Ignore" button | ||
239 | index = i + 2; | ||
240 | if (i == 0) | ||
241 | { | ||
242 | // Ignore button is smaller, less wide | ||
243 | btn_height = BTN_HEIGHT_SMALL; | ||
244 | font = sFontSmall; | ||
245 | ignore_pad = 10; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | btn_rect.setOriginAndSize(x + (index % 3) * (btn_width+HPAD+HPAD) + ignore_pad, | ||
250 | BOTTOM_PAD + (index / 3) * (BTN_HEIGHT+VPAD), | ||
251 | btn_width - 2*ignore_pad, | ||
252 | btn_height); | ||
253 | |||
254 | InstanceAndS32* userdata = new InstanceAndS32; | ||
255 | userdata->mSelf = this; | ||
256 | userdata->mButton = i; | ||
257 | |||
258 | mBtnCallbackData.put(userdata); | ||
259 | |||
260 | btn = new LLButton(options[i], btn_rect, "", onClickButton, userdata); | ||
261 | btn->setFont(font); | ||
262 | addChild(btn, -1); | ||
263 | |||
264 | if (i == mDefaultOption) | ||
265 | { | ||
266 | setDefaultBtn(btn); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | sNotifyBoxCount++; | ||
271 | |||
272 | // If this is the only notify box, don't show the next button | ||
273 | if (sNotifyBoxCount == 1 | ||
274 | && mNextBtn) | ||
275 | { | ||
276 | mNextBtn->setVisible(FALSE); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | // virtual | ||
282 | LLNotifyBox::~LLNotifyBox() | ||
283 | { | ||
284 | if (!mIsTip) | ||
285 | { | ||
286 | sNotifyBoxCount--; | ||
287 | } | ||
288 | S32 count = mBtnCallbackData.count(); | ||
289 | for (S32 i = 0; i < count; i++) | ||
290 | { | ||
291 | delete mBtnCallbackData[i]; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | // virtual | ||
296 | BOOL LLNotifyBox::handleMouseUp(S32 x, S32 y, MASK mask) | ||
297 | { | ||
298 | if (mIsTip) | ||
299 | { | ||
300 | close(); | ||
301 | return TRUE; | ||
302 | } | ||
303 | |||
304 | setFocus(TRUE); | ||
305 | |||
306 | return LLPanel::handleMouseUp(x, y, mask); | ||
307 | } | ||
308 | |||
309 | // virtual | ||
310 | BOOL LLNotifyBox::handleRightMouseDown(S32 x, S32 y, MASK mask) | ||
311 | { | ||
312 | if (!mIsTip && getVisible() && getEnabled() && pointInView(x,y)) | ||
313 | { | ||
314 | moveToBack(); | ||
315 | return TRUE; | ||
316 | } | ||
317 | |||
318 | return LLPanel::handleRightMouseDown(x, y, mask); | ||
319 | } | ||
320 | |||
321 | |||
322 | // virtual | ||
323 | void LLNotifyBox::draw() | ||
324 | { | ||
325 | F32 display_time = mTimer.getElapsedTimeF32(); | ||
326 | |||
327 | if (mAnimating && display_time < ANIMATION_TIME) | ||
328 | { | ||
329 | glMatrixMode(GL_MODELVIEW); | ||
330 | LLUI::pushMatrix(); | ||
331 | |||
332 | S32 height = mRect.getHeight(); | ||
333 | F32 fraction = display_time / ANIMATION_TIME; | ||
334 | F32 voffset = (1.f - fraction) * height; | ||
335 | if (mIsTip) voffset *= -1.f; | ||
336 | LLUI::translate(0.f, voffset, 0.f); | ||
337 | |||
338 | drawBackground(); | ||
339 | |||
340 | LLPanel::draw(); | ||
341 | |||
342 | LLUI::popMatrix(); | ||
343 | } | ||
344 | else | ||
345 | { | ||
346 | if(mAnimating) | ||
347 | { | ||
348 | mAnimating = FALSE; | ||
349 | if(!mIsTip) | ||
350 | { | ||
351 | // hide everyone behind me once I'm done animating | ||
352 | gNotifyBoxView->showOnly(this); | ||
353 | } | ||
354 | } | ||
355 | drawBackground(); | ||
356 | LLPanel::draw(); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | void LLNotifyBox::drawBackground() const | ||
361 | { | ||
362 | LLUUID image_id; | ||
363 | image_id.set(gViewerArt.getString("rounded_square.tga")); | ||
364 | LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); | ||
365 | if (imagep) | ||
366 | { | ||
367 | LLGLSTexture texture_enabled; | ||
368 | LLViewerImage::bindTexture(imagep); | ||
369 | LLColor4 color = gColors.getColor("NotifyBoxColor"); | ||
370 | if(gFocusMgr.childHasKeyboardFocus( this )) | ||
371 | { | ||
372 | const S32 focus_width = 2; | ||
373 | color = gColors.getColor("FloaterFocusBorderColor"); | ||
374 | glColor4fv(color.mV); | ||
375 | gl_segmented_rect_2d_tex(-focus_width, mRect.getHeight() + focus_width, | ||
376 | mRect.getWidth() + focus_width, -focus_width, | ||
377 | imagep->getWidth(), imagep->getHeight(), | ||
378 | 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM); | ||
379 | color = gColors.getColor("ColorDropShadow"); | ||
380 | glColor4fv(color.mV); | ||
381 | gl_segmented_rect_2d_tex(0, mRect.getHeight(), mRect.getWidth(), 0, imagep->getWidth(), imagep->getHeight(), 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM); | ||
382 | color = gColors.getColor("NotifyBoxColor"); | ||
383 | glColor4fv(color.mV); | ||
384 | 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); | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | glColor4fv(color.mV); | ||
389 | gl_segmented_rect_2d_tex(0, mRect.getHeight(), mRect.getWidth(), 0, imagep->getWidth(), imagep->getHeight(), 16, mIsTip ? ROUNDED_RECT_TOP : ROUNDED_RECT_BOTTOM); | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | |||
395 | void LLNotifyBox::close() | ||
396 | { | ||
397 | BOOL isTipTmp = mIsTip; | ||
398 | // delete this so that the sNotifyBoxCount is updated properly, so that the | ||
399 | // new frontman properly has a next button or not | ||
400 | delete this; | ||
401 | if(!isTipTmp) | ||
402 | { | ||
403 | LLNotifyBox * front = gNotifyBoxView->getFirstNontipBox(); | ||
404 | if(front) | ||
405 | { | ||
406 | gNotifyBoxView->showOnly(front); | ||
407 | // we're assuming that close is only called by user action (for non-tips), | ||
408 | // so we then give focus to the next close button | ||
409 | front->mDefaultBtn->setFocus(TRUE); | ||
410 | gFocusMgr.triggerFocusFlash(); // TODO it's ugly to call this here | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | |||
415 | /*virtual*/ | ||
416 | void LLNotifyBox::tick() | ||
417 | { | ||
418 | if (mIsTip) | ||
419 | { | ||
420 | close(); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | void LLNotifyBox::setVisible(BOOL visible) | ||
425 | { | ||
426 | // properly set the status of the next button | ||
427 | if(visible && !mIsTip) | ||
428 | { | ||
429 | mNextBtn->setVisible(sNotifyBoxCount > 1); | ||
430 | mNextBtn->setEnabled(sNotifyBoxCount > 1); | ||
431 | } | ||
432 | LLPanel::setVisible(visible); | ||
433 | } | ||
434 | |||
435 | void LLNotifyBox::moveToBack() | ||
436 | { | ||
437 | // Move this dialog to the back. | ||
438 | gNotifyBoxView->sendChildToBack(this); | ||
439 | if(!mIsTip && mNextBtn) | ||
440 | { | ||
441 | mNextBtn->setVisible(FALSE); | ||
442 | |||
443 | // And enable the next button on the frontmost one, if there is one | ||
444 | if (gNotifyBoxView->getChildCount() > 0) | ||
445 | { | ||
446 | LLNotifyBox* front = gNotifyBoxView->getFirstNontipBox(); | ||
447 | gNotifyBoxView->showOnly(front); | ||
448 | // assuming that moveToBack is only called by clicking the next button, | ||
449 | // we give focus to the next next button | ||
450 | front->mNextBtn->setFocus(TRUE); | ||
451 | gFocusMgr.triggerFocusFlash(); // TODO: it's ugly to call this here | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | |||
457 | // static | ||
458 | LLRect LLNotifyBox::getNotifyRect(S32 num_options, BOOL layout_script_dialog) | ||
459 | { | ||
460 | S32 notify_height = gSavedSettings.getS32("NotifyBoxHeight"); | ||
461 | const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth"); | ||
462 | |||
463 | const S32 TOP = gNotifyBoxView->getRect().getHeight(); | ||
464 | const S32 RIGHT = gNotifyBoxView->getRect().getWidth(); | ||
465 | const S32 LEFT = RIGHT - NOTIFY_WIDTH; | ||
466 | |||
467 | if (num_options < 1) | ||
468 | { | ||
469 | num_options = 1; | ||
470 | } | ||
471 | |||
472 | // Add two "blank" option spaces. | ||
473 | if (layout_script_dialog) | ||
474 | { | ||
475 | num_options += 2; | ||
476 | } | ||
477 | |||
478 | S32 additional_lines = (num_options-1) / 3; | ||
479 | |||
480 | notify_height += additional_lines * (BTN_HEIGHT + VPAD); | ||
481 | |||
482 | return LLRect(LEFT, TOP, RIGHT, TOP-notify_height); | ||
483 | } | ||
484 | |||
485 | // static | ||
486 | LLRect LLNotifyBox::getNotifyTipRect(const LLString &utf8message) | ||
487 | { | ||
488 | S32 line_count = 1; | ||
489 | LLWString message = utf8str_to_wstring(utf8message); | ||
490 | S32 message_len = message.length(); | ||
491 | |||
492 | const S32 NOTIFY_WIDTH = gSavedSettings.getS32("NotifyBoxWidth"); | ||
493 | // Make room for the icon area. | ||
494 | const S32 text_area_width = NOTIFY_WIDTH - HPAD * 4 - 32; | ||
495 | |||
496 | const llwchar* wchars = message.c_str(); | ||
497 | const llwchar* start = wchars; | ||
498 | const llwchar* end; | ||
499 | S32 total_drawn = 0; | ||
500 | BOOL done = FALSE; | ||
501 | |||
502 | do | ||
503 | { | ||
504 | line_count++; | ||
505 | |||
506 | for (end=start; *end != 0 && *end != '\n'; end++) | ||
507 | ; | ||
508 | |||
509 | if( *end == 0 ) | ||
510 | { | ||
511 | end = wchars + message_len; | ||
512 | done = TRUE; | ||
513 | } | ||
514 | |||
515 | S32 remaining = end - start; | ||
516 | while( remaining ) | ||
517 | { | ||
518 | S32 drawn = sFont->maxDrawableChars( start, (F32)text_area_width, remaining, TRUE ); | ||
519 | |||
520 | if( 0 == drawn ) | ||
521 | { | ||
522 | drawn = 1; // Draw at least one character, even if it doesn't all fit. (avoids an infinite loop) | ||
523 | } | ||
524 | |||
525 | total_drawn += drawn; | ||
526 | start += drawn; | ||
527 | remaining -= drawn; | ||
528 | |||
529 | if( total_drawn < message_len ) | ||
530 | { | ||
531 | if( (wchars[ total_drawn ] != '\n') ) | ||
532 | { | ||
533 | // wrap because line was too long | ||
534 | line_count++; | ||
535 | } | ||
536 | } | ||
537 | else | ||
538 | { | ||
539 | done = TRUE; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | total_drawn++; // for '\n' | ||
544 | end++; | ||
545 | start = end; | ||
546 | } while( !done ); | ||
547 | |||
548 | const S32 MIN_NOTIFY_HEIGHT = 72; | ||
549 | const S32 MAX_NOTIFY_HEIGHT = 600; | ||
550 | S32 notify_height = llceil((F32) (line_count+1) * sFont->getLineHeight()); | ||
551 | if(gOverlayBar) | ||
552 | { | ||
553 | notify_height += gOverlayBar->getRect().getHeight(); | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | // *FIX: this is derived from the padding caused by the | ||
558 | // rounded rects, shouldn't be a const here. | ||
559 | notify_height += 10; | ||
560 | } | ||
561 | notify_height += VPAD; | ||
562 | notify_height = llclamp(notify_height, MIN_NOTIFY_HEIGHT, MAX_NOTIFY_HEIGHT); | ||
563 | |||
564 | const S32 RIGHT = gNotifyBoxView->getRect().getWidth(); | ||
565 | const S32 LEFT = RIGHT - NOTIFY_WIDTH; | ||
566 | |||
567 | // Make sure it goes slightly offscreen | ||
568 | return LLRect(LEFT, notify_height-1, RIGHT, -1); | ||
569 | } | ||
570 | |||
571 | |||
572 | // static | ||
573 | void LLNotifyBox::onClickButton(void* data) | ||
574 | { | ||
575 | InstanceAndS32* self_and_button = (InstanceAndS32*)data; | ||
576 | LLNotifyBox* self = self_and_button->mSelf; | ||
577 | S32 button = self_and_button->mButton; | ||
578 | |||
579 | if (self->mCallback) | ||
580 | { | ||
581 | self->mCallback(button, self->mData); | ||
582 | } | ||
583 | |||
584 | self->close(); | ||
585 | } | ||
586 | |||
587 | |||
588 | // static | ||
589 | void LLNotifyBox::onClickNext(void* data) | ||
590 | { | ||
591 | LLNotifyBox* self = static_cast<LLNotifyBox*>(data); | ||
592 | self->moveToBack(); | ||
593 | } | ||
594 | |||
595 | //----------------------------------------------------------------------------- | ||
596 | |||
597 | //static | ||
598 | const LLString& LLNotifyBox::getTemplateMessage(const LLString& xml_desc) | ||
599 | { | ||
600 | template_map_t::iterator iter = sNotifyTemplates.find(xml_desc); | ||
601 | if (iter != sNotifyTemplates.end()) | ||
602 | { | ||
603 | return iter->second->mMessage; | ||
604 | } | ||
605 | else | ||
606 | { | ||
607 | return xml_desc; | ||
608 | } | ||
609 | } | ||
610 | |||
611 | //static | ||
612 | bool LLNotifyBox::parseNotify(const LLString& xml_filename) | ||
613 | { | ||
614 | LLXMLNodePtr root; | ||
615 | |||
616 | BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); | ||
617 | |||
618 | if (!success || root.isNull() || !root->hasName( "notifications" )) | ||
619 | { | ||
620 | llerrs << "Problem reading UI Notify file: " << xml_filename << llendl; | ||
621 | return false; | ||
622 | } | ||
623 | |||
624 | for (LLXMLNode* notify = root->getFirstChild(); | ||
625 | notify != NULL; notify = notify->getNextSibling()) | ||
626 | { | ||
627 | if (!notify->hasName("notify")) | ||
628 | { | ||
629 | continue; | ||
630 | } | ||
631 | |||
632 | LLNotifyBoxTemplate* xml_template = new LLNotifyBoxTemplate; | ||
633 | |||
634 | // label= | ||
635 | LLString notify_name; | ||
636 | if (notify->getAttributeString("name", notify_name)) | ||
637 | { | ||
638 | if (xml_template) | ||
639 | { | ||
640 | xml_template->mLabel = notify_name; | ||
641 | } | ||
642 | } | ||
643 | else | ||
644 | { | ||
645 | llwarns << "Unable to parse notify with no name" << llendl; | ||
646 | delete xml_template; | ||
647 | continue; | ||
648 | } | ||
649 | // modal= | ||
650 | BOOL tip; | ||
651 | if (notify->getAttributeBOOL("tip", tip)) | ||
652 | { | ||
653 | if (xml_template) | ||
654 | { | ||
655 | xml_template->mIsTip = tip; | ||
656 | } | ||
657 | } | ||
658 | |||
659 | S32 btn_idx = 0; | ||
660 | for (LLXMLNode* child = notify->getFirstChild(); | ||
661 | child != NULL; child = child->getNextSibling()) | ||
662 | { | ||
663 | // <message> | ||
664 | if (child->hasName("message")) | ||
665 | { | ||
666 | if (xml_template) | ||
667 | { | ||
668 | xml_template->mMessage = child->getTextContents(); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | // <option> | ||
673 | if (child->hasName("option")) | ||
674 | { | ||
675 | LLString label = child->getValue(); | ||
676 | BOOL is_default = FALSE; | ||
677 | child->getAttributeBOOL("default", is_default); | ||
678 | LLString ignore_text; | ||
679 | if (!child->getAttributeString("ignore", ignore_text)) | ||
680 | { | ||
681 | ignore_text = label; | ||
682 | } | ||
683 | if (xml_template) | ||
684 | { | ||
685 | xml_template->addOption(label, is_default); | ||
686 | } | ||
687 | btn_idx++; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | //XUI:translate | ||
692 | if (xml_template->mOptions.empty()) | ||
693 | { | ||
694 | xml_template->addOption("OK", FALSE); | ||
695 | } | ||
696 | if (xml_template) | ||
697 | { | ||
698 | sNotifyTemplates[xml_template->mLabel] = xml_template; | ||
699 | } | ||
700 | } | ||
701 | return true; | ||
702 | } | ||
703 | |||
704 | LLNotifyBoxView::LLNotifyBoxView(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows) | ||
705 | : LLUICtrl(name,rect,mouse_opaque,NULL,NULL,follows) | ||
706 | { | ||
707 | } | ||
708 | |||
709 | LLNotifyBox * LLNotifyBoxView::getFirstNontipBox() const | ||
710 | { | ||
711 | // assumes every child is a notify box | ||
712 | for(child_list_const_iter_t iter = getChildList()->begin(); | ||
713 | iter != getChildList()->end(); | ||
714 | iter++) | ||
715 | { | ||
716 | LLNotifyBox * box = static_cast<LLNotifyBox*>(*iter); | ||
717 | if(!box->isTip()) | ||
718 | { | ||
719 | return box; | ||
720 | } | ||
721 | } | ||
722 | return NULL; | ||
723 | } | ||
724 | |||
725 | void LLNotifyBoxView::showOnly(LLView * view) | ||
726 | { | ||
727 | if(view) | ||
728 | { | ||
729 | // assumes that the argument is actually a child | ||
730 | LLNotifyBox * shown = static_cast<LLNotifyBox*>(view); | ||
731 | // make every other notification invisible | ||
732 | for(child_list_const_iter_t iter = getChildList()->begin(); | ||
733 | iter != getChildList()->end(); | ||
734 | iter++) | ||
735 | { | ||
736 | LLNotifyBox * box = static_cast<LLNotifyBox*>(*iter); | ||
737 | if(box != view && box->getVisible() && !box->isTip()) | ||
738 | { | ||
739 | box->setVisible(FALSE); | ||
740 | } | ||
741 | } | ||
742 | shown->setVisible(TRUE); | ||
743 | sendChildToFront(shown); | ||
744 | } | ||
745 | } | ||