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/llui/llmenugl.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 'linden/indra/llui/llmenugl.cpp')
-rw-r--r-- | linden/indra/llui/llmenugl.cpp | 4636 |
1 files changed, 4636 insertions, 0 deletions
diff --git a/linden/indra/llui/llmenugl.cpp b/linden/indra/llui/llmenugl.cpp new file mode 100644 index 0000000..6d70fd1 --- /dev/null +++ b/linden/indra/llui/llmenugl.cpp | |||
@@ -0,0 +1,4636 @@ | |||
1 | /** | ||
2 | * @file llmenugl.cpp | ||
3 | * @brief LLMenuItemGL base class | ||
4 | * | ||
5 | * Copyright (c) 2001-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 | //***************************************************************************** | ||
29 | // | ||
30 | // This file contains the opengl based menu implementation. | ||
31 | // | ||
32 | // NOTES: A menu label is split into 4 columns. The left column, the | ||
33 | // label colum, the accelerator column, and the right column. The left | ||
34 | // column is used for displaying boolean values for toggle and check | ||
35 | // controls. The right column is used for submenus. | ||
36 | // | ||
37 | //***************************************************************************** | ||
38 | |||
39 | //#include "llviewerprecompiledheaders.h" | ||
40 | #include "linden_common.h" | ||
41 | |||
42 | #include "llmenugl.h" | ||
43 | |||
44 | #include "llmath.h" | ||
45 | #include "llgl.h" | ||
46 | #include "llfocusmgr.h" | ||
47 | #include "llfont.h" | ||
48 | #include "llcoord.h" | ||
49 | #include "llwindow.h" | ||
50 | #include "llcriticaldamp.h" | ||
51 | #include "lluictrlfactory.h" | ||
52 | |||
53 | #include "llfontgl.h" | ||
54 | #include "llresmgr.h" | ||
55 | #include "llui.h" | ||
56 | |||
57 | #include "llglheaders.h" | ||
58 | #include "llstl.h" | ||
59 | |||
60 | #include "v2math.h" | ||
61 | #include <set> | ||
62 | #include <boost/tokenizer.hpp> | ||
63 | |||
64 | // static | ||
65 | LLView *LLMenuGL::sDefaultMenuContainer = NULL; | ||
66 | |||
67 | S32 MENU_BAR_HEIGHT = 0; | ||
68 | S32 MENU_BAR_WIDTH = 0; | ||
69 | |||
70 | ///============================================================================ | ||
71 | /// Local function declarations, constants, enums, and typedefs | ||
72 | ///============================================================================ | ||
73 | |||
74 | const LLString SEPARATOR_NAME("separator"); | ||
75 | const LLString TEAROFF_SEPARATOR_LABEL( "~~~~~~~~~~~" ); | ||
76 | const LLString SEPARATOR_LABEL( "-----------" ); | ||
77 | const LLString VERTICAL_SEPARATOR_LABEL( "|" ); | ||
78 | |||
79 | const S32 LABEL_BOTTOM_PAD_PIXELS = 2; | ||
80 | |||
81 | const U32 LEFT_PAD_PIXELS = 3; | ||
82 | const U32 LEFT_WIDTH_PIXELS = 15; | ||
83 | const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS; | ||
84 | |||
85 | const U32 RIGHT_PAD_PIXELS = 2; | ||
86 | const U32 RIGHT_WIDTH_PIXELS = 15; | ||
87 | const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; | ||
88 | |||
89 | const U32 ACCEL_PAD_PIXELS = 10; | ||
90 | const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; | ||
91 | |||
92 | const U32 BRIEF_PAD_PIXELS = 2; | ||
93 | |||
94 | const U32 SEPARATOR_HEIGHT_PIXELS = 8; | ||
95 | const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; | ||
96 | const S32 MENU_ITEM_PADDING = 4; | ||
97 | |||
98 | const LLString BOOLEAN_TRUE_PREFIX( "X" ); | ||
99 | const LLString BRANCH_SUFFIX( ">" ); | ||
100 | const LLString ARROW_UP ("^^^^^^^"); | ||
101 | const LLString ARROW_DOWN("vvvvvvv"); | ||
102 | |||
103 | const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f; | ||
104 | |||
105 | const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10; | ||
106 | |||
107 | LLColor4 LLMenuItemGL::sEnabledColor( 0.0f, 0.0f, 0.0f, 1.0f ); | ||
108 | LLColor4 LLMenuItemGL::sDisabledColor( 0.5f, 0.5f, 0.5f, 1.0f ); | ||
109 | LLColor4 LLMenuItemGL::sHighlightBackground( 0.0f, 0.0f, 0.7f, 1.0f ); | ||
110 | LLColor4 LLMenuItemGL::sHighlightForeground( 1.0f, 1.0f, 1.0f, 1.0f ); | ||
111 | BOOL LLMenuItemGL::sDropShadowText = TRUE; | ||
112 | |||
113 | LLColor4 LLMenuGL::sDefaultBackgroundColor( 0.25f, 0.25f, 0.25f, 0.75f ); | ||
114 | BOOL LLMenuGL::sKeyboardMode = FALSE; | ||
115 | |||
116 | LLViewHandle LLMenuHolderGL::sItemLastSelectedHandle; | ||
117 | LLFrameTimer LLMenuHolderGL::sItemActivationTimer; | ||
118 | //LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f ); | ||
119 | |||
120 | const S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole | ||
121 | const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down | ||
122 | const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu | ||
123 | |||
124 | const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; | ||
125 | |||
126 | ///============================================================================ | ||
127 | /// Class LLMenuItemGL | ||
128 | ///============================================================================ | ||
129 | |||
130 | // Default constructor | ||
131 | LLMenuItemGL::LLMenuItemGL( const LLString& name, const LLString& label, KEY key, MASK mask ) : | ||
132 | LLView( name, TRUE ), | ||
133 | mJumpKey(KEY_NONE), | ||
134 | mAcceleratorKey( key ), | ||
135 | mAcceleratorMask( mask ), | ||
136 | mAllowKeyRepeat(FALSE), | ||
137 | mHighlight( FALSE ), | ||
138 | mGotHover( FALSE ), | ||
139 | mBriefItem( FALSE ), | ||
140 | mFont( LLFontGL::sSansSerif ), | ||
141 | mStyle(LLFontGL::NORMAL), | ||
142 | mDrawTextDisabled( FALSE ) | ||
143 | { | ||
144 | setLabel( label ); | ||
145 | } | ||
146 | |||
147 | // virtual | ||
148 | LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const | ||
149 | { | ||
150 | LLXMLNodePtr node = LLView::getXML(); | ||
151 | |||
152 | node->createChild("type", TRUE)->setStringValue(getType()); | ||
153 | |||
154 | node->createChild("label", TRUE)->setStringValue(mLabel); | ||
155 | |||
156 | if (mAcceleratorKey != KEY_NONE) | ||
157 | { | ||
158 | std::stringstream out; | ||
159 | if (mAcceleratorMask & MASK_CONTROL) | ||
160 | { | ||
161 | out << "control|"; | ||
162 | } | ||
163 | if (mAcceleratorMask & MASK_ALT) | ||
164 | { | ||
165 | out << "alt|"; | ||
166 | } | ||
167 | if (mAcceleratorMask & MASK_SHIFT) | ||
168 | { | ||
169 | out << "shift|"; | ||
170 | } | ||
171 | out << LLKeyboard::stringFromKey(mAcceleratorKey); | ||
172 | |||
173 | node->createChild("shortcut", TRUE)->setStringValue(out.str()); | ||
174 | } | ||
175 | |||
176 | return node; | ||
177 | } | ||
178 | |||
179 | BOOL LLMenuItemGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) | ||
180 | { | ||
181 | // modified from LLView::handleKey | ||
182 | // ignore visibility, as keyboard accelerators should still trigger menu items | ||
183 | // even when they are not visible | ||
184 | // also, ignore enabled flag for self, as that can change based on menu callbacks | ||
185 | BOOL handled = FALSE; | ||
186 | |||
187 | if( called_from_parent ) | ||
188 | { | ||
189 | // Downward traversal | ||
190 | if (mEnabled) | ||
191 | { | ||
192 | handled = childrenHandleKey( key, mask ) != NULL; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | if( !handled ) | ||
197 | { | ||
198 | handled = handleKeyHere( key, mask, called_from_parent ); | ||
199 | } | ||
200 | |||
201 | return handled; | ||
202 | } | ||
203 | |||
204 | BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) | ||
205 | { | ||
206 | if( mEnabled && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == mAcceleratorMask) ) | ||
207 | { | ||
208 | doIt(); | ||
209 | return TRUE; | ||
210 | } | ||
211 | return FALSE; | ||
212 | } | ||
213 | |||
214 | BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) | ||
215 | { | ||
216 | mGotHover = TRUE; | ||
217 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
218 | return TRUE; | ||
219 | } | ||
220 | |||
221 | void LLMenuItemGL::setBriefItem(BOOL b) | ||
222 | { | ||
223 | mBriefItem = b; | ||
224 | } | ||
225 | |||
226 | // This function checks to see if the accelerator key is already in use; | ||
227 | // if not, it will be added to the list | ||
228 | BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) | ||
229 | { | ||
230 | LLKeyBinding *accelerator = NULL; | ||
231 | |||
232 | if (mAcceleratorKey != KEY_NONE) | ||
233 | { | ||
234 | std::list<LLKeyBinding*>::iterator list_it; | ||
235 | for (list_it = listp->begin(); list_it != listp->end(); ++list_it) | ||
236 | { | ||
237 | accelerator = *list_it; | ||
238 | if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == mAcceleratorMask)) | ||
239 | { | ||
240 | |||
241 | // *NOTE: get calling code to throw up warning or route | ||
242 | // warning messages back to app-provided output | ||
243 | // LLString warning; | ||
244 | // warning.append("Duplicate key binding <"); | ||
245 | // appendAcceleratorString( warning ); | ||
246 | // warning.append("> for menu items:\n "); | ||
247 | // warning.append(accelerator->mName); | ||
248 | // warning.append("\n "); | ||
249 | // warning.append(mLabel); | ||
250 | |||
251 | // llwarns << warning << llendl; | ||
252 | // LLAlertDialog::modalAlert(warning); | ||
253 | return FALSE; | ||
254 | } | ||
255 | } | ||
256 | if (!accelerator) | ||
257 | { | ||
258 | accelerator = new LLKeyBinding; | ||
259 | if (accelerator) | ||
260 | { | ||
261 | accelerator->mKey = mAcceleratorKey; | ||
262 | accelerator->mMask = mAcceleratorMask; | ||
263 | // accelerator->mName = mLabel; | ||
264 | } | ||
265 | listp->push_back(accelerator);//addData(accelerator); | ||
266 | } | ||
267 | } | ||
268 | return TRUE; | ||
269 | } | ||
270 | |||
271 | // This function appends the character string representation of | ||
272 | // the current accelerator key and mask to the provided string. | ||
273 | void LLMenuItemGL::appendAcceleratorString( LLString& st ) | ||
274 | { | ||
275 | // break early if this is a silly thing to do. | ||
276 | if( KEY_NONE == mAcceleratorKey ) | ||
277 | { | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | // Append any masks | ||
282 | #ifdef LL_DARWIN | ||
283 | // Standard Mac names for modifier keys in menu equivalents | ||
284 | // We could use the symbol characters, but they only exist in certain fonts. | ||
285 | if( mAcceleratorMask & MASK_CONTROL ) | ||
286 | st.append( "Cmd-" ); // Symbol would be "\xE2\x8C\x98" | ||
287 | if( mAcceleratorMask & MASK_ALT ) | ||
288 | st.append( "Opt-" ); // Symbol would be "\xE2\x8C\xA5" | ||
289 | if( mAcceleratorMask & MASK_SHIFT ) | ||
290 | st.append( "Shift-" ); // Symbol would be "\xE2\x8C\xA7" | ||
291 | #else | ||
292 | if( mAcceleratorMask & MASK_CONTROL ) | ||
293 | st.append( "Ctrl-" ); | ||
294 | if( mAcceleratorMask & MASK_ALT ) | ||
295 | st.append( "Alt-" ); | ||
296 | if( mAcceleratorMask & MASK_SHIFT ) | ||
297 | st.append( "Shift-" ); | ||
298 | #endif | ||
299 | |||
300 | LLString keystr = LLKeyboard::stringFromKey( mAcceleratorKey ); | ||
301 | if ((mAcceleratorMask & (MASK_CONTROL|MASK_ALT|MASK_SHIFT)) && | ||
302 | (keystr[0] == '-' || keystr[0] == '=')) | ||
303 | { | ||
304 | st.append( " " ); | ||
305 | } | ||
306 | st.append( keystr ); | ||
307 | } | ||
308 | |||
309 | void LLMenuItemGL::setJumpKey(KEY key) | ||
310 | { | ||
311 | mJumpKey = LLStringOps::toUpper((char)key); | ||
312 | } | ||
313 | |||
314 | KEY LLMenuItemGL::getJumpKey() | ||
315 | { | ||
316 | return mJumpKey; | ||
317 | } | ||
318 | |||
319 | |||
320 | // set the font used by all of the menu objects | ||
321 | void LLMenuItemGL::setFont(LLFontGL* font) | ||
322 | { | ||
323 | mFont = font; | ||
324 | } | ||
325 | |||
326 | // returns the height in pixels for the current font. | ||
327 | U32 LLMenuItemGL::getNominalHeight( void ) | ||
328 | { | ||
329 | return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; | ||
330 | } | ||
331 | |||
332 | // functions to control the color scheme | ||
333 | void LLMenuItemGL::setEnabledColor( const LLColor4& color ) | ||
334 | { | ||
335 | sEnabledColor = color; | ||
336 | } | ||
337 | |||
338 | void LLMenuItemGL::setDisabledColor( const LLColor4& color ) | ||
339 | { | ||
340 | sDisabledColor = color; | ||
341 | } | ||
342 | |||
343 | void LLMenuItemGL::setHighlightBGColor( const LLColor4& color ) | ||
344 | { | ||
345 | sHighlightBackground = color; | ||
346 | } | ||
347 | |||
348 | void LLMenuItemGL::setHighlightFGColor( const LLColor4& color ) | ||
349 | { | ||
350 | sHighlightForeground = color; | ||
351 | } | ||
352 | |||
353 | |||
354 | // change the label | ||
355 | void LLMenuItemGL::setLabel( const LLString& label ) | ||
356 | { | ||
357 | mLabel = label; | ||
358 | } | ||
359 | |||
360 | // Get the parent menu for this item | ||
361 | LLMenuGL* LLMenuItemGL::getMenu() | ||
362 | { | ||
363 | return (LLMenuGL*) getParent(); | ||
364 | } | ||
365 | |||
366 | |||
367 | // getNominalWidth() - returns the normal width of this control in | ||
368 | // pixels - this is used for calculating the widest item, as well as | ||
369 | // for horizontal arrangement. | ||
370 | U32 LLMenuItemGL::getNominalWidth( void ) | ||
371 | { | ||
372 | U32 width; | ||
373 | |||
374 | if (mBriefItem) | ||
375 | { | ||
376 | width = BRIEF_PAD_PIXELS; | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | width = PLAIN_PAD_PIXELS; | ||
381 | } | ||
382 | |||
383 | if( KEY_NONE != mAcceleratorKey ) | ||
384 | { | ||
385 | width += ACCEL_PAD_PIXELS; | ||
386 | LLString temp; | ||
387 | appendAcceleratorString( temp ); | ||
388 | width += mFont->getWidth( temp ); | ||
389 | } | ||
390 | width += mFont->getWidth( mLabel.getWString().c_str() ); | ||
391 | return width; | ||
392 | } | ||
393 | |||
394 | // called to rebuild the draw label | ||
395 | void LLMenuItemGL::buildDrawLabel( void ) | ||
396 | { | ||
397 | mDrawAccelLabel.clear(); | ||
398 | LLString st = mDrawAccelLabel.getString(); | ||
399 | appendAcceleratorString( st ); | ||
400 | mDrawAccelLabel = st; | ||
401 | } | ||
402 | |||
403 | void LLMenuItemGL::doIt( void ) | ||
404 | { | ||
405 | // close all open menus by default | ||
406 | // if parent menu is actually visible (and we are not triggering menu item via accelerator) | ||
407 | // HACK: do not call hidemenus() from a pie menu item, as most pie menu operations | ||
408 | // assume that the thing you clicked on stays selected (parcel and/or object) after the | ||
409 | // pie menu is gone --RN | ||
410 | if (getMenu()->getWidgetType() != WIDGET_TYPE_PIE_MENU | ||
411 | && !getMenu()->getTornOff() | ||
412 | && getMenu()->getVisible()) | ||
413 | { | ||
414 | ((LLMenuHolderGL*)getMenu()->getParent())->hideMenus(); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | // set the hover status (called by it's menu) | ||
419 | void LLMenuItemGL::setHighlight( BOOL highlight ) | ||
420 | { | ||
421 | if (highlight) | ||
422 | { | ||
423 | getMenu()->clearHoverItem(); | ||
424 | } | ||
425 | mHighlight = highlight; | ||
426 | } | ||
427 | |||
428 | // determine if this object represents an active sub-menu | ||
429 | BOOL LLMenuItemGL::isActive( void ) const | ||
430 | { | ||
431 | return FALSE; | ||
432 | } | ||
433 | |||
434 | // determine if this object represents an open sub-menu | ||
435 | BOOL LLMenuItemGL::isOpen( void ) const | ||
436 | { | ||
437 | return FALSE; | ||
438 | } | ||
439 | |||
440 | BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) | ||
441 | { | ||
442 | if (getHighlight() && | ||
443 | getMenu()->isOpen()) | ||
444 | { | ||
445 | if (key == KEY_UP) | ||
446 | { | ||
447 | // switch to keyboard navigation mode | ||
448 | LLMenuGL::setKeyboardMode(TRUE); | ||
449 | |||
450 | getMenu()->highlightPrevItem(this); | ||
451 | return TRUE; | ||
452 | } | ||
453 | else if (key == KEY_DOWN) | ||
454 | { | ||
455 | // switch to keyboard navigation mode | ||
456 | LLMenuGL::setKeyboardMode(TRUE); | ||
457 | |||
458 | getMenu()->highlightNextItem(this); | ||
459 | return TRUE; | ||
460 | } | ||
461 | else if (key == KEY_RETURN && mask == MASK_NONE) | ||
462 | { | ||
463 | // switch to keyboard navigation mode | ||
464 | LLMenuGL::setKeyboardMode(TRUE); | ||
465 | |||
466 | doIt(); | ||
467 | return TRUE; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | return FALSE; | ||
472 | } | ||
473 | |||
474 | BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK ) | ||
475 | { | ||
476 | //llinfos << mLabel.c_str() << " handleMouseUp " << x << "," << y | ||
477 | // << llendl; | ||
478 | if (mEnabled) | ||
479 | { | ||
480 | // switch to mouse navigation mode | ||
481 | LLMenuGL::setKeyboardMode(FALSE); | ||
482 | |||
483 | doIt(); | ||
484 | setHighlight(FALSE); | ||
485 | make_ui_sound("UISndClickRelease"); | ||
486 | return TRUE; | ||
487 | } | ||
488 | else | ||
489 | { | ||
490 | return FALSE; | ||
491 | } | ||
492 | } | ||
493 | |||
494 | void LLMenuItemGL::draw( void ) | ||
495 | { | ||
496 | // *FIX: This can be optimized by using switches. Want to avoid | ||
497 | // that until the functionality is finalized. | ||
498 | |||
499 | // HACK: Brief items don't highlight. Pie menu takes care of it. JC | ||
500 | // let disabled items be highlighted, just don't draw them as such | ||
501 | if( getEnabled() && getHighlight() && !mBriefItem) | ||
502 | { | ||
503 | glColor4fv( sHighlightBackground.mV ); | ||
504 | gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
505 | } | ||
506 | |||
507 | LLColor4 color; | ||
508 | |||
509 | U8 font_style = mStyle; | ||
510 | if (LLMenuItemGL::sDropShadowText && getEnabled() && !mDrawTextDisabled ) | ||
511 | { | ||
512 | font_style |= LLFontGL::DROP_SHADOW; | ||
513 | } | ||
514 | |||
515 | if ( getEnabled() && getHighlight() ) | ||
516 | { | ||
517 | color = sHighlightForeground; | ||
518 | } | ||
519 | else if( getEnabled() && !mDrawTextDisabled ) | ||
520 | { | ||
521 | color = sEnabledColor; | ||
522 | } | ||
523 | else | ||
524 | { | ||
525 | color = sDisabledColor; | ||
526 | } | ||
527 | |||
528 | // Draw the text on top. | ||
529 | if (mBriefItem) | ||
530 | { | ||
531 | mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color, | ||
532 | LLFontGL::LEFT, LLFontGL::BOTTOM, font_style ); | ||
533 | } | ||
534 | else | ||
535 | { | ||
536 | if( !mDrawBoolLabel.empty() ) | ||
537 | { | ||
538 | mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, | ||
539 | LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE ); | ||
540 | } | ||
541 | mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, | ||
542 | LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE ); | ||
543 | if( !mDrawAccelLabel.empty() ) | ||
544 | { | ||
545 | mFont->render( mDrawAccelLabel.getWString(), 0, (F32)mRect.mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, | ||
546 | LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE ); | ||
547 | } | ||
548 | if( !mDrawBranchLabel.empty() ) | ||
549 | { | ||
550 | mFont->render( mDrawBranchLabel.getWString(), 0, (F32)mRect.mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, | ||
551 | LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE ); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | // underline "jump" key only when keyboard navigation has been initiated | ||
556 | if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) | ||
557 | { | ||
558 | LLString upper_case_label = mLabel.getString(); | ||
559 | LLString::toUpper(upper_case_label); | ||
560 | std::string::size_type offset = upper_case_label.find(mJumpKey); | ||
561 | if (offset != std::string::npos) | ||
562 | { | ||
563 | S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset); | ||
564 | S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1); | ||
565 | gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | // clear got hover every frame | ||
570 | mGotHover = FALSE; | ||
571 | } | ||
572 | |||
573 | BOOL LLMenuItemGL::setLabelArg( const LLString& key, const LLString& text ) | ||
574 | { | ||
575 | mLabel.setArg(key, text); | ||
576 | return TRUE; | ||
577 | } | ||
578 | |||
579 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
580 | // Class LLMenuItemSeparatorGL | ||
581 | // | ||
582 | // This class represents a separator. | ||
583 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
584 | |||
585 | class LLMenuItemSeparatorGL : public LLMenuItemGL | ||
586 | { | ||
587 | public: | ||
588 | LLMenuItemSeparatorGL( const LLString &name = SEPARATOR_NAME ); | ||
589 | |||
590 | virtual LLString getType() const { return "separator"; } | ||
591 | |||
592 | virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_SEPARATOR; } | ||
593 | virtual LLString getWidgetTag() const { return LL_MENU_ITEM_SEPARATOR_GL_TAG; } | ||
594 | |||
595 | // doIt() - do the primary funcationality of the menu item. | ||
596 | virtual void doIt( void ) {} | ||
597 | |||
598 | virtual void draw( void ); | ||
599 | virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); | ||
600 | virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); | ||
601 | virtual BOOL handleHover(S32 x, S32 y, MASK mask); | ||
602 | |||
603 | virtual U32 getNominalHeight( void ) { return SEPARATOR_HEIGHT_PIXELS; } | ||
604 | }; | ||
605 | |||
606 | LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const LLString &name ) : | ||
607 | LLMenuItemGL( SEPARATOR_NAME, SEPARATOR_LABEL ) | ||
608 | { | ||
609 | } | ||
610 | |||
611 | void LLMenuItemSeparatorGL::draw( void ) | ||
612 | { | ||
613 | glColor4fv( sDisabledColor.mV ); | ||
614 | const S32 y = mRect.getHeight() / 2; | ||
615 | const S32 PAD = 6; | ||
616 | gl_line_2d( PAD, y, mRect.getWidth() - PAD, y ); | ||
617 | } | ||
618 | |||
619 | BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) | ||
620 | { | ||
621 | LLMenuGL* parent_menu = getMenu(); | ||
622 | if (y > mRect.getHeight() / 2) | ||
623 | { | ||
624 | return parent_menu->handleMouseDown(x + mRect.mLeft, mRect.mTop + 1, mask); | ||
625 | } | ||
626 | else | ||
627 | { | ||
628 | return parent_menu->handleMouseDown(x + mRect.mLeft, mRect.mBottom - 1, mask); | ||
629 | } | ||
630 | } | ||
631 | |||
632 | BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) | ||
633 | { | ||
634 | LLMenuGL* parent_menu = getMenu(); | ||
635 | if (y > mRect.getHeight() / 2) | ||
636 | { | ||
637 | return parent_menu->handleMouseUp(x + mRect.mLeft, mRect.mTop + 1, mask); | ||
638 | } | ||
639 | else | ||
640 | { | ||
641 | return parent_menu->handleMouseUp(x + mRect.mLeft, mRect.mBottom - 1, mask); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) | ||
646 | { | ||
647 | LLMenuGL* parent_menu = getMenu(); | ||
648 | if (y > mRect.getHeight() / 2) | ||
649 | { | ||
650 | parent_menu->highlightPrevItem(this, FALSE); | ||
651 | return FALSE; | ||
652 | } | ||
653 | else | ||
654 | { | ||
655 | parent_menu->highlightNextItem(this, FALSE); | ||
656 | return FALSE; | ||
657 | } | ||
658 | } | ||
659 | |||
660 | |||
661 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
662 | // Class LLMenuItemVerticalSeparatorGL | ||
663 | // | ||
664 | // This class represents a vertical separator. | ||
665 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
666 | |||
667 | class LLMenuItemVerticalSeparatorGL | ||
668 | : public LLMenuItemSeparatorGL | ||
669 | { | ||
670 | public: | ||
671 | LLMenuItemVerticalSeparatorGL( void ); | ||
672 | |||
673 | virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_SEPARATOR_VERTICAL; } | ||
674 | virtual LLString getWidgetTag() const { return LL_MENU_ITEM_VERTICAL_SEPARATOR_GL_TAG; } | ||
675 | |||
676 | virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } | ||
677 | }; | ||
678 | |||
679 | LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) | ||
680 | { | ||
681 | setLabel( VERTICAL_SEPARATOR_LABEL ); | ||
682 | } | ||
683 | |||
684 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
685 | // Class LLMenuItemTearOffGL | ||
686 | // | ||
687 | // This class represents a separator. | ||
688 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
689 | LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLViewHandle parent_floater_handle) : | ||
690 | LLMenuItemGL("tear off", TEAROFF_SEPARATOR_LABEL), | ||
691 | mParentHandle(parent_floater_handle) | ||
692 | { | ||
693 | } | ||
694 | |||
695 | EWidgetType LLMenuItemTearOffGL::getWidgetType() const | ||
696 | { | ||
697 | return WIDGET_TYPE_TEAROFF_MENU; | ||
698 | } | ||
699 | |||
700 | LLString LLMenuItemTearOffGL::getWidgetTag() const | ||
701 | { | ||
702 | return LL_MENU_ITEM_TEAR_OFF_GL_TAG; | ||
703 | } | ||
704 | |||
705 | void LLMenuItemTearOffGL::doIt() | ||
706 | { | ||
707 | if (getMenu()->getTornOff()) | ||
708 | { | ||
709 | LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent()); | ||
710 | torn_off_menu->close(); | ||
711 | } | ||
712 | else | ||
713 | { | ||
714 | // transfer keyboard focus and highlight to first real item in list | ||
715 | if (getHighlight()) | ||
716 | { | ||
717 | getMenu()->highlightNextItem(this); | ||
718 | } | ||
719 | |||
720 | getMenu()->arrange(); | ||
721 | |||
722 | LLFloater* parent_floater = LLFloater::getFloaterByHandle(mParentHandle); | ||
723 | LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); | ||
724 | if (parent_floater && tear_off_menu) | ||
725 | { | ||
726 | parent_floater->addDependentFloater(tear_off_menu, FALSE); | ||
727 | } | ||
728 | |||
729 | // give focus to torn off menu because it will have been taken away | ||
730 | // when parent menu closes | ||
731 | tear_off_menu->setFocus(TRUE); | ||
732 | } | ||
733 | LLMenuItemGL::doIt(); | ||
734 | } | ||
735 | |||
736 | void LLMenuItemTearOffGL::draw() | ||
737 | { | ||
738 | // disabled items can be highlighted, but shouldn't render as such | ||
739 | if( getEnabled() && getHighlight() && !mBriefItem) | ||
740 | { | ||
741 | glColor4fv( sHighlightBackground.mV ); | ||
742 | gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
743 | } | ||
744 | |||
745 | if (mEnabled) | ||
746 | { | ||
747 | glColor4fv( sEnabledColor.mV ); | ||
748 | } | ||
749 | else | ||
750 | { | ||
751 | glColor4fv( sDisabledColor.mV ); | ||
752 | } | ||
753 | const S32 y = mRect.getHeight() / 3; | ||
754 | const S32 PAD = 6; | ||
755 | gl_line_2d( PAD, y, mRect.getWidth() - PAD, y ); | ||
756 | gl_line_2d( PAD, y * 2, mRect.getWidth() - PAD, y * 2 ); | ||
757 | } | ||
758 | |||
759 | U32 LLMenuItemTearOffGL::getNominalHeight( void ) { return TEAROFF_SEPARATOR_HEIGHT_PIXELS; } | ||
760 | |||
761 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
762 | // Class LLMenuItemBlankGL | ||
763 | // | ||
764 | // This class represents a blank, non-functioning item. | ||
765 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
766 | |||
767 | class LLMenuItemBlankGL : public LLMenuItemGL | ||
768 | { | ||
769 | public: | ||
770 | LLMenuItemBlankGL( void ); | ||
771 | |||
772 | virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_BLANK; } | ||
773 | virtual LLString getWidgetTag() const { return LL_MENU_ITEM_BLANK_GL_TAG; } | ||
774 | |||
775 | // doIt() - do the primary funcationality of the menu item. | ||
776 | virtual void doIt( void ) {} | ||
777 | |||
778 | virtual void draw( void ) {} | ||
779 | }; | ||
780 | |||
781 | LLMenuItemBlankGL::LLMenuItemBlankGL( void ) | ||
782 | : LLMenuItemGL( "", "" ) | ||
783 | { | ||
784 | mEnabled = FALSE; | ||
785 | } | ||
786 | |||
787 | ///============================================================================ | ||
788 | /// Class LLMenuItemCallGL | ||
789 | ///============================================================================ | ||
790 | |||
791 | LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name, | ||
792 | const LLString& label, | ||
793 | menu_callback clicked_cb, | ||
794 | enabled_callback enabled_cb, | ||
795 | void* user_data, | ||
796 | KEY key, MASK mask, | ||
797 | BOOL enabled, | ||
798 | on_disabled_callback on_disabled_cb) : | ||
799 | LLMenuItemGL( name, label, key, mask ), | ||
800 | mCallback( clicked_cb ), | ||
801 | mEnabledCallback( enabled_cb ), | ||
802 | mLabelCallback(NULL), | ||
803 | mUserData( user_data ), | ||
804 | mOnDisabledCallback(on_disabled_cb) | ||
805 | { | ||
806 | if(!enabled) setEnabled(FALSE); | ||
807 | } | ||
808 | |||
809 | LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name, | ||
810 | menu_callback clicked_cb, | ||
811 | enabled_callback enabled_cb, | ||
812 | void* user_data, | ||
813 | KEY key, MASK mask, | ||
814 | BOOL enabled, | ||
815 | on_disabled_callback on_disabled_cb) : | ||
816 | LLMenuItemGL( name, name, key, mask ), | ||
817 | mCallback( clicked_cb ), | ||
818 | mEnabledCallback( enabled_cb ), | ||
819 | mLabelCallback(NULL), | ||
820 | mUserData( user_data ), | ||
821 | mOnDisabledCallback(on_disabled_cb) | ||
822 | { | ||
823 | if(!enabled) setEnabled(FALSE); | ||
824 | } | ||
825 | |||
826 | LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name, | ||
827 | const LLString& label, | ||
828 | menu_callback clicked_cb, | ||
829 | enabled_callback enabled_cb, | ||
830 | label_callback label_cb, | ||
831 | void* user_data, | ||
832 | KEY key, MASK mask, | ||
833 | BOOL enabled, | ||
834 | on_disabled_callback on_disabled_cb) : | ||
835 | LLMenuItemGL(name, label, key, mask), | ||
836 | mCallback(clicked_cb), | ||
837 | mEnabledCallback(enabled_cb), | ||
838 | mLabelCallback(label_cb), | ||
839 | mUserData(user_data), | ||
840 | mOnDisabledCallback(on_disabled_cb) | ||
841 | { | ||
842 | if(!enabled) setEnabled(FALSE); | ||
843 | } | ||
844 | |||
845 | LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name, | ||
846 | menu_callback clicked_cb, | ||
847 | enabled_callback enabled_cb, | ||
848 | label_callback label_cb, | ||
849 | void* user_data, | ||
850 | KEY key, MASK mask, | ||
851 | BOOL enabled, | ||
852 | on_disabled_callback on_disabled_cb) : | ||
853 | LLMenuItemGL(name, name, key, mask), | ||
854 | mCallback(clicked_cb), | ||
855 | mEnabledCallback(enabled_cb), | ||
856 | mLabelCallback(label_cb), | ||
857 | mUserData(user_data), | ||
858 | mOnDisabledCallback(on_disabled_cb) | ||
859 | { | ||
860 | if(!enabled) setEnabled(FALSE); | ||
861 | } | ||
862 | |||
863 | void LLMenuItemCallGL::setEnabledControl(LLString enabled_control, LLView *context) | ||
864 | { | ||
865 | // Register new listener | ||
866 | if (!enabled_control.empty()) | ||
867 | { | ||
868 | LLControlBase *control = context->findControl(enabled_control); | ||
869 | if (control) | ||
870 | { | ||
871 | LLSD state = control->registerListener(this, "ENABLED"); | ||
872 | setEnabled(state); | ||
873 | } | ||
874 | else | ||
875 | { | ||
876 | context->addBoolControl(enabled_control, mEnabled); | ||
877 | control = context->findControl(enabled_control); | ||
878 | control->registerListener(this, "ENABLED"); | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | |||
883 | void LLMenuItemCallGL::setVisibleControl(LLString enabled_control, LLView *context) | ||
884 | { | ||
885 | // Register new listener | ||
886 | if (!enabled_control.empty()) | ||
887 | { | ||
888 | LLControlBase *control = context->findControl(enabled_control); | ||
889 | if (control) | ||
890 | { | ||
891 | LLSD state = control->registerListener(this, "VISIBLE"); | ||
892 | setVisible(state); | ||
893 | } | ||
894 | else | ||
895 | { | ||
896 | context->addBoolControl(enabled_control, mEnabled); | ||
897 | control = context->findControl(enabled_control); | ||
898 | control->registerListener(this, "VISIBLE"); | ||
899 | } | ||
900 | } | ||
901 | } | ||
902 | |||
903 | // virtual | ||
904 | bool LLMenuItemCallGL::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
905 | { | ||
906 | if (userdata.asString() == "ENABLED" && event->desc() == "value_changed") | ||
907 | { | ||
908 | LLSD state = event->getValue(); | ||
909 | setEnabled(state); | ||
910 | return TRUE; | ||
911 | } | ||
912 | if (userdata.asString() == "VISIBLE" && event->desc() == "value_changed") | ||
913 | { | ||
914 | LLSD state = event->getValue(); | ||
915 | setVisible(state); | ||
916 | return TRUE; | ||
917 | } | ||
918 | return LLMenuItemGL::handleEvent(event, userdata); | ||
919 | } | ||
920 | |||
921 | // virtual | ||
922 | LLXMLNodePtr LLMenuItemCallGL::getXML(bool save_children) const | ||
923 | { | ||
924 | LLXMLNodePtr node = LLMenuItemGL::getXML(); | ||
925 | |||
926 | // Contents | ||
927 | |||
928 | std::vector<LLListenerEntry> listeners = mDispatcher->getListeners(); | ||
929 | std::vector<LLListenerEntry>::iterator itor; | ||
930 | for (itor = listeners.begin(); itor != listeners.end(); ++itor) | ||
931 | { | ||
932 | LLString listener_name = findEventListener((LLSimpleListener*)itor->listener); | ||
933 | if (!listener_name.empty()) | ||
934 | { | ||
935 | LLXMLNodePtr child_node = node->createChild("on_click", FALSE); | ||
936 | child_node->createChild("function", TRUE)->setStringValue(listener_name); | ||
937 | child_node->createChild("filter", TRUE)->setStringValue(itor->filter.asString()); | ||
938 | child_node->createChild("userdata", TRUE)->setStringValue(itor->userdata.asString()); | ||
939 | } | ||
940 | } | ||
941 | |||
942 | return node; | ||
943 | } | ||
944 | |||
945 | // doIt() - Call the callback provided | ||
946 | void LLMenuItemCallGL::doIt( void ) | ||
947 | { | ||
948 | // RN: menu item can be deleted in callback, so beware | ||
949 | getMenu()->setItemLastSelected( this ); | ||
950 | |||
951 | if( mCallback ) | ||
952 | { | ||
953 | mCallback( mUserData ); | ||
954 | } | ||
955 | LLPointer<LLEvent> fired_event = new LLEvent(this); | ||
956 | fireEvent(fired_event, "on_click"); | ||
957 | LLMenuItemGL::doIt(); | ||
958 | } | ||
959 | |||
960 | EWidgetType LLMenuItemCallGL::getWidgetType() const | ||
961 | { | ||
962 | return WIDGET_TYPE_MENU_ITEM_CALL; | ||
963 | } | ||
964 | |||
965 | LLString LLMenuItemCallGL::getWidgetTag() const | ||
966 | { | ||
967 | return LL_MENU_ITEM_CALL_GL_TAG; | ||
968 | } | ||
969 | |||
970 | void LLMenuItemCallGL::buildDrawLabel( void ) | ||
971 | { | ||
972 | LLPointer<LLEvent> fired_event = new LLEvent(this); | ||
973 | fireEvent(fired_event, "on_build"); | ||
974 | if( mEnabledCallback ) | ||
975 | { | ||
976 | setEnabled( mEnabledCallback( mUserData ) ); | ||
977 | } | ||
978 | if(mLabelCallback) | ||
979 | { | ||
980 | LLString label; | ||
981 | mLabelCallback(label, mUserData); | ||
982 | mLabel = label; | ||
983 | } | ||
984 | LLMenuItemGL::buildDrawLabel(); | ||
985 | } | ||
986 | |||
987 | BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) | ||
988 | { | ||
989 | if( (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == mAcceleratorMask) ) | ||
990 | { | ||
991 | LLPointer<LLEvent> fired_event = new LLEvent(this); | ||
992 | fireEvent(fired_event, "on_build"); | ||
993 | if( mEnabledCallback ) | ||
994 | { | ||
995 | setEnabled( mEnabledCallback( mUserData ) ); | ||
996 | } | ||
997 | if( !mEnabled ) | ||
998 | { | ||
999 | if( mOnDisabledCallback ) | ||
1000 | { | ||
1001 | mOnDisabledCallback( mUserData ); | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | return LLMenuItemGL::handleAcceleratorKey(key, mask); | ||
1006 | } | ||
1007 | |||
1008 | ///============================================================================ | ||
1009 | /// Class LLMenuItemCheckGL | ||
1010 | ///============================================================================ | ||
1011 | |||
1012 | LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, | ||
1013 | const LLString& label, | ||
1014 | menu_callback clicked_cb, | ||
1015 | enabled_callback enabled_cb, | ||
1016 | check_callback check_cb, | ||
1017 | void* user_data, | ||
1018 | KEY key, MASK mask ) : | ||
1019 | LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ), | ||
1020 | mCheckCallback( check_cb ), | ||
1021 | mChecked(FALSE) | ||
1022 | { | ||
1023 | } | ||
1024 | |||
1025 | LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, | ||
1026 | menu_callback clicked_cb, | ||
1027 | enabled_callback enabled_cb, | ||
1028 | check_callback check_cb, | ||
1029 | void* user_data, | ||
1030 | KEY key, MASK mask ) : | ||
1031 | LLMenuItemCallGL( name, name, clicked_cb, enabled_cb, user_data, key, mask ), | ||
1032 | mCheckCallback( check_cb ), | ||
1033 | mChecked(FALSE) | ||
1034 | { | ||
1035 | } | ||
1036 | |||
1037 | LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, | ||
1038 | const LLString& label, | ||
1039 | menu_callback clicked_cb, | ||
1040 | enabled_callback enabled_cb, | ||
1041 | LLString control_name, | ||
1042 | LLView *context, | ||
1043 | void* user_data, | ||
1044 | KEY key, MASK mask ) : | ||
1045 | LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ), | ||
1046 | mCheckCallback( NULL ) | ||
1047 | { | ||
1048 | setControlName(control_name, context); | ||
1049 | } | ||
1050 | |||
1051 | void LLMenuItemCheckGL::setCheckedControl(LLString checked_control, LLView *context) | ||
1052 | { | ||
1053 | // Register new listener | ||
1054 | if (!checked_control.empty()) | ||
1055 | { | ||
1056 | LLControlBase *control = context->findControl(checked_control); | ||
1057 | if (control) | ||
1058 | { | ||
1059 | LLSD state = control->registerListener(this, "CHECKED"); | ||
1060 | mChecked = state; | ||
1061 | } | ||
1062 | else | ||
1063 | { | ||
1064 | context->addBoolControl(checked_control, mChecked); | ||
1065 | control = context->findControl(checked_control); | ||
1066 | control->registerListener(this, "CHECKED"); | ||
1067 | } | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | // virtual | ||
1072 | bool LLMenuItemCheckGL::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
1073 | { | ||
1074 | if (userdata.asString() == "CHECKED" && event->desc() == "value_changed") | ||
1075 | { | ||
1076 | LLSD state = event->getValue(); | ||
1077 | mChecked = state; | ||
1078 | if(mChecked) | ||
1079 | { | ||
1080 | mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; | ||
1081 | } | ||
1082 | else | ||
1083 | { | ||
1084 | mDrawBoolLabel.clear(); | ||
1085 | } | ||
1086 | return TRUE; | ||
1087 | } | ||
1088 | return LLMenuItemCallGL::handleEvent(event, userdata); | ||
1089 | } | ||
1090 | |||
1091 | // virtual | ||
1092 | LLXMLNodePtr LLMenuItemCheckGL::getXML(bool save_children) const | ||
1093 | { | ||
1094 | LLXMLNodePtr node = LLMenuItemCallGL::getXML(); | ||
1095 | return node; | ||
1096 | } | ||
1097 | |||
1098 | EWidgetType LLMenuItemCheckGL::getWidgetType() const | ||
1099 | { | ||
1100 | return WIDGET_TYPE_MENU_ITEM_CHECK; | ||
1101 | } | ||
1102 | |||
1103 | LLString LLMenuItemCheckGL::getWidgetTag() const | ||
1104 | { | ||
1105 | return LL_MENU_ITEM_CHECK_GL_TAG; | ||
1106 | } | ||
1107 | |||
1108 | // called to rebuild the draw label | ||
1109 | void LLMenuItemCheckGL::buildDrawLabel( void ) | ||
1110 | { | ||
1111 | if(mChecked || (mCheckCallback && mCheckCallback( mUserData ) ) ) | ||
1112 | { | ||
1113 | mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; | ||
1114 | } | ||
1115 | else | ||
1116 | { | ||
1117 | mDrawBoolLabel.clear(); | ||
1118 | } | ||
1119 | LLMenuItemCallGL::buildDrawLabel(); | ||
1120 | } | ||
1121 | |||
1122 | |||
1123 | ///============================================================================ | ||
1124 | /// Class LLMenuItemToggleGL | ||
1125 | ///============================================================================ | ||
1126 | |||
1127 | LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, const LLString& label, BOOL* toggle, | ||
1128 | KEY key, MASK mask ) : | ||
1129 | LLMenuItemGL( name, label, key, mask ), | ||
1130 | mToggle( toggle ) | ||
1131 | { | ||
1132 | } | ||
1133 | |||
1134 | LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, BOOL* toggle, | ||
1135 | KEY key, MASK mask ) : | ||
1136 | LLMenuItemGL( name, name, key, mask ), | ||
1137 | mToggle( toggle ) | ||
1138 | { | ||
1139 | } | ||
1140 | |||
1141 | |||
1142 | // called to rebuild the draw label | ||
1143 | void LLMenuItemToggleGL::buildDrawLabel( void ) | ||
1144 | { | ||
1145 | if( *mToggle ) | ||
1146 | { | ||
1147 | mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; | ||
1148 | } | ||
1149 | else | ||
1150 | { | ||
1151 | mDrawBoolLabel.clear(); | ||
1152 | } | ||
1153 | mDrawAccelLabel.clear(); | ||
1154 | LLString st = mDrawAccelLabel; | ||
1155 | appendAcceleratorString( st ); | ||
1156 | mDrawAccelLabel = st; | ||
1157 | } | ||
1158 | |||
1159 | // doIt() - do the primary funcationality of the menu item. | ||
1160 | void LLMenuItemToggleGL::doIt( void ) | ||
1161 | { | ||
1162 | getMenu()->setItemLastSelected( this ); | ||
1163 | //llinfos << "LLMenuItemToggleGL::doIt " << mLabel.c_str() << llendl; | ||
1164 | *mToggle = !(*mToggle); | ||
1165 | buildDrawLabel(); | ||
1166 | LLMenuItemGL::doIt(); | ||
1167 | } | ||
1168 | |||
1169 | |||
1170 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
1171 | // Class LLMenuItemBranchGL | ||
1172 | // | ||
1173 | // The LLMenuItemBranchGL represents a menu item that has a | ||
1174 | // sub-menu. This is used to make cascading menus. | ||
1175 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
1176 | |||
1177 | class LLMenuItemBranchGL : public LLMenuItemGL | ||
1178 | { | ||
1179 | protected: | ||
1180 | LLMenuGL* mBranch; | ||
1181 | |||
1182 | public: | ||
1183 | LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch, | ||
1184 | KEY key = KEY_NONE, MASK mask = MASK_NONE ); | ||
1185 | virtual LLXMLNodePtr getXML(bool save_children = true) const; | ||
1186 | |||
1187 | virtual LLView* getChildByName(const LLString& name, BOOL recurse) const; | ||
1188 | |||
1189 | virtual LLString getType() const { return "menu"; } | ||
1190 | |||
1191 | virtual EWidgetType getWidgetType() const; | ||
1192 | virtual LLString getWidgetTag() const; | ||
1193 | |||
1194 | virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); | ||
1195 | |||
1196 | virtual BOOL handleAcceleratorKey(KEY key, MASK mask); | ||
1197 | |||
1198 | // check if we've used these accelerators already | ||
1199 | virtual BOOL addToAcceleratorList(std::list <LLKeyBinding*> *listp); | ||
1200 | |||
1201 | // called to rebuild the draw label | ||
1202 | virtual void buildDrawLabel( void ); | ||
1203 | |||
1204 | // doIt() - do the primary funcationality of the menu item. | ||
1205 | virtual void doIt( void ); | ||
1206 | |||
1207 | virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); | ||
1208 | virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); | ||
1209 | |||
1210 | // set the hover status (called by it's menu) and if the object is | ||
1211 | // active. This is used for behavior transfer. | ||
1212 | virtual void setHighlight( BOOL highlight ); | ||
1213 | |||
1214 | virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); | ||
1215 | |||
1216 | virtual BOOL isActive() const; | ||
1217 | |||
1218 | virtual BOOL isOpen() const; | ||
1219 | |||
1220 | LLMenuGL *getBranch() const { return mBranch; } | ||
1221 | |||
1222 | virtual void updateBranchParent( LLView* parentp ); | ||
1223 | |||
1224 | // LLView Functionality | ||
1225 | virtual void onVisibilityChange( BOOL curVisibilityIn ); | ||
1226 | |||
1227 | virtual void draw(); | ||
1228 | |||
1229 | virtual void setEnabledSubMenus(BOOL enabled); | ||
1230 | |||
1231 | virtual void openMenu(); | ||
1232 | }; | ||
1233 | |||
1234 | LLMenuItemBranchGL::LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch, | ||
1235 | KEY key, MASK mask ) : | ||
1236 | LLMenuItemGL( name, label, key, mask ), | ||
1237 | mBranch( branch ) | ||
1238 | { | ||
1239 | mBranch->setVisible( FALSE ); | ||
1240 | mBranch->setParentMenuItem(this); | ||
1241 | } | ||
1242 | |||
1243 | // virtual | ||
1244 | LLView* LLMenuItemBranchGL::getChildByName(const LLString& name, BOOL recurse) const | ||
1245 | { | ||
1246 | if (mBranch->getName() == name) | ||
1247 | { | ||
1248 | return mBranch; | ||
1249 | } | ||
1250 | // Always recurse on branches | ||
1251 | return mBranch->getChildByName(name, recurse); | ||
1252 | } | ||
1253 | |||
1254 | EWidgetType LLMenuItemBranchGL::getWidgetType() const | ||
1255 | { | ||
1256 | return WIDGET_TYPE_MENU_ITEM_BRANCH; | ||
1257 | } | ||
1258 | |||
1259 | LLString LLMenuItemBranchGL::getWidgetTag() const | ||
1260 | { | ||
1261 | return LL_MENU_ITEM_BRANCH_GL_TAG; | ||
1262 | } | ||
1263 | |||
1264 | // virtual | ||
1265 | BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) | ||
1266 | { | ||
1267 | if (mEnabled) | ||
1268 | { | ||
1269 | // switch to mouse navigation mode | ||
1270 | LLMenuGL::setKeyboardMode(FALSE); | ||
1271 | |||
1272 | doIt(); | ||
1273 | make_ui_sound("UISndClickRelease"); | ||
1274 | } | ||
1275 | return FALSE; | ||
1276 | } | ||
1277 | |||
1278 | BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) | ||
1279 | { | ||
1280 | return mBranch->handleAcceleratorKey(key, mask); | ||
1281 | } | ||
1282 | |||
1283 | // virtual | ||
1284 | LLXMLNodePtr LLMenuItemBranchGL::getXML(bool save_children) const | ||
1285 | { | ||
1286 | if (mBranch) | ||
1287 | { | ||
1288 | return mBranch->getXML(); | ||
1289 | } | ||
1290 | |||
1291 | return LLMenuItemGL::getXML(); | ||
1292 | } | ||
1293 | |||
1294 | |||
1295 | // This function checks to see if the accelerator key is already in use; | ||
1296 | // if not, it will be added to the list | ||
1297 | BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp) | ||
1298 | { | ||
1299 | U32 item_count = mBranch->getItemCount(); | ||
1300 | LLMenuItemGL *item; | ||
1301 | |||
1302 | while (item_count--) | ||
1303 | { | ||
1304 | if ((item = mBranch->getItem(item_count))) | ||
1305 | { | ||
1306 | return item->addToAcceleratorList(listp); | ||
1307 | } | ||
1308 | } | ||
1309 | return FALSE; | ||
1310 | } | ||
1311 | |||
1312 | |||
1313 | // called to rebuild the draw label | ||
1314 | void LLMenuItemBranchGL::buildDrawLabel( void ) | ||
1315 | { | ||
1316 | mDrawAccelLabel.clear(); | ||
1317 | LLString st = mDrawAccelLabel; | ||
1318 | appendAcceleratorString( st ); | ||
1319 | mDrawAccelLabel = st; | ||
1320 | mDrawBranchLabel = BRANCH_SUFFIX; | ||
1321 | } | ||
1322 | |||
1323 | // doIt() - do the primary functionality of the menu item. | ||
1324 | void LLMenuItemBranchGL::doIt( void ) | ||
1325 | { | ||
1326 | openMenu(); | ||
1327 | |||
1328 | // keyboard navigation automatically propagates highlight to sub-menu | ||
1329 | // to facilitate fast menu control via jump keys | ||
1330 | if (LLMenuGL::getKeyboardMode() && !mBranch->getHighlightedItem()) | ||
1331 | { | ||
1332 | mBranch->highlightNextItem(NULL); | ||
1333 | } | ||
1334 | } | ||
1335 | |||
1336 | BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) | ||
1337 | { | ||
1338 | BOOL handled = FALSE; | ||
1339 | if (called_from_parent) | ||
1340 | { | ||
1341 | handled = mBranch->handleKey(key, mask, called_from_parent); | ||
1342 | } | ||
1343 | |||
1344 | if (!handled) | ||
1345 | { | ||
1346 | handled = LLMenuItemGL::handleKey(key, mask, called_from_parent); | ||
1347 | } | ||
1348 | |||
1349 | return handled; | ||
1350 | } | ||
1351 | |||
1352 | BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) | ||
1353 | { | ||
1354 | BOOL handled = FALSE; | ||
1355 | if (called_from_parent) | ||
1356 | { | ||
1357 | handled = mBranch->handleUnicodeChar(uni_char, TRUE); | ||
1358 | } | ||
1359 | |||
1360 | if (!handled) | ||
1361 | { | ||
1362 | handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent); | ||
1363 | } | ||
1364 | |||
1365 | return handled; | ||
1366 | } | ||
1367 | |||
1368 | |||
1369 | // set the hover status (called by it's menu) | ||
1370 | void LLMenuItemBranchGL::setHighlight( BOOL highlight ) | ||
1371 | { | ||
1372 | if (highlight == getHighlight()) return; | ||
1373 | |||
1374 | // make sure only yourself is highlighted | ||
1375 | if (highlight) | ||
1376 | { | ||
1377 | getMenu()->clearHoverItem(); | ||
1378 | } | ||
1379 | |||
1380 | BOOL auto_open = mEnabled && (!mBranch->getVisible() || mBranch->getTornOff()); | ||
1381 | // torn off menus don't open sub menus on hover unless they have focus | ||
1382 | if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus()) | ||
1383 | { | ||
1384 | auto_open = FALSE; | ||
1385 | } | ||
1386 | // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) | ||
1387 | if (mBranch->getTornOff()) | ||
1388 | { | ||
1389 | auto_open = FALSE; | ||
1390 | } | ||
1391 | |||
1392 | mHighlight = highlight; | ||
1393 | if( highlight ) | ||
1394 | { | ||
1395 | if(auto_open) | ||
1396 | { | ||
1397 | openMenu(); | ||
1398 | } | ||
1399 | } | ||
1400 | else | ||
1401 | { | ||
1402 | if (mBranch->getTornOff()) | ||
1403 | { | ||
1404 | ((LLFloater*)mBranch->getParent())->setFocus(FALSE); | ||
1405 | mBranch->clearHoverItem(); | ||
1406 | } | ||
1407 | else | ||
1408 | { | ||
1409 | mBranch->setVisible( FALSE ); | ||
1410 | } | ||
1411 | } | ||
1412 | } | ||
1413 | |||
1414 | void LLMenuItemBranchGL::setEnabledSubMenus(BOOL enabled) | ||
1415 | { | ||
1416 | mBranch->setEnabledSubMenus(enabled); | ||
1417 | } | ||
1418 | |||
1419 | void LLMenuItemBranchGL::draw() | ||
1420 | { | ||
1421 | LLMenuItemGL::draw(); | ||
1422 | if (mBranch->getVisible() && !mBranch->getTornOff()) | ||
1423 | { | ||
1424 | setHighlight(TRUE); | ||
1425 | } | ||
1426 | } | ||
1427 | |||
1428 | // determine if this object is active | ||
1429 | // which, for branching menus, means the branch is open and has "focus" | ||
1430 | BOOL LLMenuItemBranchGL::isActive( void ) const | ||
1431 | { | ||
1432 | return isOpen() && mBranch->getHighlightedItem(); | ||
1433 | } | ||
1434 | |||
1435 | BOOL LLMenuItemBranchGL::isOpen( void ) const | ||
1436 | { | ||
1437 | return mBranch->isOpen(); | ||
1438 | } | ||
1439 | |||
1440 | void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) | ||
1441 | { | ||
1442 | if (mBranch->getParent() == NULL) | ||
1443 | { | ||
1444 | // make the branch menu a sibling of my parent menu | ||
1445 | mBranch->updateParent(parentp); | ||
1446 | } | ||
1447 | } | ||
1448 | |||
1449 | void LLMenuItemBranchGL::onVisibilityChange( BOOL curVisibilityIn ) | ||
1450 | { | ||
1451 | if (curVisibilityIn == FALSE && mBranch->getVisible() && !mBranch->getTornOff()) | ||
1452 | { | ||
1453 | mBranch->setVisible(FALSE); | ||
1454 | } | ||
1455 | } | ||
1456 | |||
1457 | BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) | ||
1458 | { | ||
1459 | if (getMenu()->getVisible() && mBranch->getVisible() && key == KEY_LEFT) | ||
1460 | { | ||
1461 | // switch to keyboard navigation mode | ||
1462 | LLMenuGL::setKeyboardMode(TRUE); | ||
1463 | |||
1464 | BOOL handled = mBranch->clearHoverItem(); | ||
1465 | if (mBranch->getTornOff()) | ||
1466 | { | ||
1467 | ((LLFloater*)mBranch->getParent())->setFocus(FALSE); | ||
1468 | } | ||
1469 | if (handled && getMenu()->getTornOff()) | ||
1470 | { | ||
1471 | ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); | ||
1472 | } | ||
1473 | return handled; | ||
1474 | } | ||
1475 | |||
1476 | if (getEnabled() && | ||
1477 | getHighlight() && | ||
1478 | getMenu()->isOpen() && | ||
1479 | key == KEY_RIGHT && !mBranch->getHighlightedItem()) | ||
1480 | { | ||
1481 | // switch to keyboard navigation mode | ||
1482 | LLMenuGL::setKeyboardMode(TRUE); | ||
1483 | |||
1484 | LLMenuItemGL* itemp = mBranch->highlightNextItem(NULL); | ||
1485 | if (itemp) | ||
1486 | { | ||
1487 | return TRUE; | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | return LLMenuItemGL::handleKeyHere(key, mask, called_from_parent); | ||
1492 | } | ||
1493 | |||
1494 | void LLMenuItemBranchGL::openMenu() | ||
1495 | { | ||
1496 | if (mBranch->getTornOff()) | ||
1497 | { | ||
1498 | gFloaterView->bringToFront((LLFloater*)mBranch->getParent()); | ||
1499 | // this might not be necessary, as torn off branches don't get focus and hence no highligth | ||
1500 | mBranch->highlightNextItem(NULL); | ||
1501 | } | ||
1502 | else if( !mBranch->getVisible() ) | ||
1503 | { | ||
1504 | mBranch->arrange(); | ||
1505 | |||
1506 | LLRect rect = mBranch->getRect(); | ||
1507 | // calculate root-view relative position for branch menu | ||
1508 | S32 left = mRect.mRight; | ||
1509 | S32 top = mRect.mTop - mRect.mBottom; | ||
1510 | |||
1511 | localPointToOtherView(left, top, &left, &top, mBranch->getParent()); | ||
1512 | |||
1513 | rect.setLeftTopAndSize( left, top, | ||
1514 | rect.getWidth(), rect.getHeight() ); | ||
1515 | |||
1516 | if (mBranch->getCanTearOff()) | ||
1517 | { | ||
1518 | rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); | ||
1519 | } | ||
1520 | mBranch->setRect( rect ); | ||
1521 | S32 x = 0; | ||
1522 | S32 y = 0; | ||
1523 | mBranch->localPointToOtherView( 0, 0, &x, &y, mBranch->getParent() ); | ||
1524 | S32 delta_x = 0; | ||
1525 | S32 delta_y = 0; | ||
1526 | if( y < 0 ) | ||
1527 | { | ||
1528 | delta_y = -y; | ||
1529 | } | ||
1530 | |||
1531 | S32 window_width = mBranch->getParent()->getRect().getWidth(); | ||
1532 | if( x > window_width - rect.getWidth() ) | ||
1533 | { | ||
1534 | // move sub-menu over to left side | ||
1535 | delta_x = llmax(-x, (-1 * (rect.getWidth() + mRect.getWidth()))); | ||
1536 | } | ||
1537 | mBranch->translate( delta_x, delta_y ); | ||
1538 | mBranch->setVisible( TRUE ); | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | |||
1543 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
1544 | // Class LLMenuItemBranchDownGL | ||
1545 | // | ||
1546 | // The LLMenuItemBranchDownGL represents a menu item that has a | ||
1547 | // sub-menu. This is used to make menu bar menus. | ||
1548 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
1549 | |||
1550 | class LLMenuItemBranchDownGL : public LLMenuItemBranchGL | ||
1551 | { | ||
1552 | protected: | ||
1553 | |||
1554 | public: | ||
1555 | LLMenuItemBranchDownGL( const LLString& name, const LLString& label, LLMenuGL* branch, | ||
1556 | KEY key = KEY_NONE, MASK mask = MASK_NONE ); | ||
1557 | |||
1558 | virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_BRANCH_DOWN; } | ||
1559 | virtual LLString getWidgetTag() const { return LL_MENU_ITEM_BRANCH_DOWN_GL_TAG; } | ||
1560 | |||
1561 | virtual LLString getType() const { return "menu"; } | ||
1562 | |||
1563 | // returns the normal width of this control in pixels - this is | ||
1564 | // used for calculating the widest item, as well as for horizontal | ||
1565 | // arrangement. | ||
1566 | virtual U32 getNominalWidth( void ); | ||
1567 | |||
1568 | // called to rebuild the draw label | ||
1569 | virtual void buildDrawLabel( void ); | ||
1570 | |||
1571 | // handles opening, positioning, and arranging the menu branch associated with this item | ||
1572 | virtual void openMenu( void ); | ||
1573 | |||
1574 | // set the hover status (called by it's menu) and if the object is | ||
1575 | // active. This is used for behavior transfer. | ||
1576 | virtual void setHighlight( BOOL highlight ); | ||
1577 | |||
1578 | virtual BOOL isActive( void ) const; | ||
1579 | |||
1580 | // LLView functionality | ||
1581 | virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); | ||
1582 | virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ) {return FALSE; } | ||
1583 | virtual void draw( void ); | ||
1584 | virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); | ||
1585 | |||
1586 | virtual BOOL handleAcceleratorKey(KEY key, MASK mask); | ||
1587 | }; | ||
1588 | |||
1589 | LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const LLString& name, | ||
1590 | const LLString& label, | ||
1591 | LLMenuGL* branch, | ||
1592 | KEY key, MASK mask ) : | ||
1593 | LLMenuItemBranchGL( name, label, branch, key, mask ) | ||
1594 | { | ||
1595 | } | ||
1596 | |||
1597 | // returns the normal width of this control in pixels - this is used | ||
1598 | // for calculating the widest item, as well as for horizontal | ||
1599 | // arrangement. | ||
1600 | U32 LLMenuItemBranchDownGL::getNominalWidth( void ) | ||
1601 | { | ||
1602 | U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS; | ||
1603 | width += mFont->getWidth( mLabel.getWString().c_str() ); | ||
1604 | return width; | ||
1605 | } | ||
1606 | |||
1607 | // called to rebuild the draw label | ||
1608 | void LLMenuItemBranchDownGL::buildDrawLabel( void ) | ||
1609 | { | ||
1610 | mDrawAccelLabel.clear(); | ||
1611 | LLString st = mDrawAccelLabel; | ||
1612 | appendAcceleratorString( st ); | ||
1613 | mDrawAccelLabel = st; | ||
1614 | } | ||
1615 | |||
1616 | void LLMenuItemBranchDownGL::openMenu( void ) | ||
1617 | { | ||
1618 | if( mBranch->getVisible() && !mBranch->getTornOff() ) | ||
1619 | { | ||
1620 | mBranch->setVisible( FALSE ); | ||
1621 | } | ||
1622 | else | ||
1623 | { | ||
1624 | if (mBranch->getTornOff()) | ||
1625 | { | ||
1626 | gFloaterView->bringToFront((LLFloater*)mBranch->getParent()); | ||
1627 | } | ||
1628 | else | ||
1629 | { | ||
1630 | // We're showing the drop-down menu, so patch up its labels/rects | ||
1631 | mBranch->arrange(); | ||
1632 | |||
1633 | LLRect rect = mBranch->getRect(); | ||
1634 | S32 left = 0; | ||
1635 | S32 top = mRect.mBottom; | ||
1636 | localPointToOtherView(left, top, &left, &top, mBranch->getParent()); | ||
1637 | |||
1638 | rect.setLeftTopAndSize( left, top, | ||
1639 | rect.getWidth(), rect.getHeight() ); | ||
1640 | mBranch->setRect( rect ); | ||
1641 | S32 x = 0; | ||
1642 | S32 y = 0; | ||
1643 | mBranch->localPointToScreen( 0, 0, &x, &y ); | ||
1644 | S32 delta_x = 0; | ||
1645 | |||
1646 | LLCoordScreen window_size; | ||
1647 | LLWindow* windowp = getWindow(); | ||
1648 | windowp->getSize(&window_size); | ||
1649 | |||
1650 | S32 window_width = window_size.mX; | ||
1651 | if( x > window_width - rect.getWidth() ) | ||
1652 | { | ||
1653 | delta_x = (window_width - rect.getWidth()) - x; | ||
1654 | } | ||
1655 | mBranch->translate( delta_x, 0 ); | ||
1656 | |||
1657 | setHighlight(TRUE); | ||
1658 | mBranch->setVisible( TRUE ); | ||
1659 | } | ||
1660 | |||
1661 | |||
1662 | } | ||
1663 | } | ||
1664 | |||
1665 | // set the hover status (called by it's menu) | ||
1666 | void LLMenuItemBranchDownGL::setHighlight( BOOL highlight ) | ||
1667 | { | ||
1668 | if (highlight == getHighlight()) return; | ||
1669 | |||
1670 | if (highlight) | ||
1671 | { | ||
1672 | getMenu()->clearHoverItem(); | ||
1673 | } | ||
1674 | mHighlight = highlight; | ||
1675 | if( !highlight) | ||
1676 | { | ||
1677 | if (mBranch->getTornOff()) | ||
1678 | { | ||
1679 | ((LLFloater*)mBranch->getParent())->setFocus(FALSE); | ||
1680 | mBranch->clearHoverItem(); | ||
1681 | } | ||
1682 | else | ||
1683 | { | ||
1684 | mBranch->setVisible( FALSE ); | ||
1685 | } | ||
1686 | } | ||
1687 | } | ||
1688 | |||
1689 | BOOL LLMenuItemBranchDownGL::isActive() const | ||
1690 | { | ||
1691 | // for top level menus, being open is sufficient to be considered | ||
1692 | // active, because clicking on them with the mouse will open | ||
1693 | // them, without moving keyboard focus to them | ||
1694 | return isOpen(); | ||
1695 | } | ||
1696 | |||
1697 | BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
1698 | { | ||
1699 | // switch to mouse control mode | ||
1700 | LLMenuGL::setKeyboardMode(FALSE); | ||
1701 | doIt(); | ||
1702 | make_ui_sound("UISndClick"); | ||
1703 | return TRUE; | ||
1704 | } | ||
1705 | |||
1706 | |||
1707 | BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) | ||
1708 | { | ||
1709 | BOOL branch_visible = mBranch->getVisible(); | ||
1710 | BOOL handled = mBranch->handleAcceleratorKey(key, mask); | ||
1711 | if (handled && !branch_visible) | ||
1712 | { | ||
1713 | // flash this menu entry because we triggered an invisible menu item | ||
1714 | LLMenuHolderGL::setActivatedItem(this); | ||
1715 | } | ||
1716 | |||
1717 | return handled; | ||
1718 | } | ||
1719 | |||
1720 | BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) | ||
1721 | { | ||
1722 | BOOL menu_open = mBranch->getVisible(); | ||
1723 | if (getHighlight() && getMenu()->getVisible()) | ||
1724 | { | ||
1725 | if (key == KEY_LEFT) | ||
1726 | { | ||
1727 | // switch to keyboard navigation mode | ||
1728 | LLMenuGL::setKeyboardMode(TRUE); | ||
1729 | |||
1730 | LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); | ||
1731 | // open new menu only if previous menu was open | ||
1732 | if (itemp && itemp->getEnabled() && menu_open) | ||
1733 | { | ||
1734 | itemp->doIt(); | ||
1735 | } | ||
1736 | |||
1737 | return TRUE; | ||
1738 | } | ||
1739 | else if (key == KEY_RIGHT) | ||
1740 | { | ||
1741 | // switch to keyboard navigation mode | ||
1742 | LLMenuGL::setKeyboardMode(TRUE); | ||
1743 | |||
1744 | LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); | ||
1745 | // open new menu only if previous menu was open | ||
1746 | if (itemp && itemp->getEnabled() && menu_open) | ||
1747 | { | ||
1748 | itemp->doIt(); | ||
1749 | } | ||
1750 | |||
1751 | return TRUE; | ||
1752 | } | ||
1753 | else if (key == KEY_DOWN) | ||
1754 | { | ||
1755 | // switch to keyboard navigation mode | ||
1756 | LLMenuGL::setKeyboardMode(TRUE); | ||
1757 | |||
1758 | if (getEnabled() && !isActive()) | ||
1759 | { | ||
1760 | doIt(); | ||
1761 | } | ||
1762 | mBranch->highlightNextItem(NULL); | ||
1763 | return TRUE; | ||
1764 | } | ||
1765 | else if (key == KEY_UP) | ||
1766 | { | ||
1767 | // switch to keyboard navigation mode | ||
1768 | LLMenuGL::setKeyboardMode(TRUE); | ||
1769 | |||
1770 | if (getEnabled() && !isActive()) | ||
1771 | { | ||
1772 | doIt(); | ||
1773 | } | ||
1774 | mBranch->highlightPrevItem(NULL); | ||
1775 | return TRUE; | ||
1776 | } | ||
1777 | } | ||
1778 | |||
1779 | return FALSE; | ||
1780 | } | ||
1781 | |||
1782 | void LLMenuItemBranchDownGL::draw( void ) | ||
1783 | { | ||
1784 | //FIXME: try removing this | ||
1785 | if (mBranch->getVisible() && !mBranch->getTornOff()) | ||
1786 | { | ||
1787 | setHighlight(TRUE); | ||
1788 | } | ||
1789 | |||
1790 | if( getHighlight() ) | ||
1791 | { | ||
1792 | glColor4fv( sHighlightBackground.mV ); | ||
1793 | gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
1794 | } | ||
1795 | |||
1796 | U8 font_style = mStyle; | ||
1797 | if (LLMenuItemGL::sDropShadowText && getEnabled() && !mDrawTextDisabled ) | ||
1798 | { | ||
1799 | font_style |= LLFontGL::DROP_SHADOW; | ||
1800 | } | ||
1801 | |||
1802 | LLColor4 color; | ||
1803 | if (getHighlight()) | ||
1804 | { | ||
1805 | color = sHighlightForeground; | ||
1806 | } | ||
1807 | else if( mEnabled ) | ||
1808 | { | ||
1809 | color = sEnabledColor; | ||
1810 | } | ||
1811 | else | ||
1812 | { | ||
1813 | color = sDisabledColor; | ||
1814 | } | ||
1815 | mFont->render( mLabel.getWString(), 0, (F32)mRect.getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, | ||
1816 | LLFontGL::HCENTER, LLFontGL::BOTTOM, font_style ); | ||
1817 | |||
1818 | |||
1819 | // underline navigation key only when keyboard navigation has been initiated | ||
1820 | if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) | ||
1821 | { | ||
1822 | LLString upper_case_label = mLabel.getString(); | ||
1823 | LLString::toUpper(upper_case_label); | ||
1824 | std::string::size_type offset = upper_case_label.find(mJumpKey); | ||
1825 | if (offset != std::string::npos) | ||
1826 | { | ||
1827 | S32 x_offset = llround((F32)mRect.getWidth() / 2.f - mFont->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); | ||
1828 | S32 x_begin = x_offset + mFont->getWidth(mLabel, 0, offset); | ||
1829 | S32 x_end = x_offset + mFont->getWidth(mLabel, 0, offset + 1); | ||
1830 | gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); | ||
1831 | } | ||
1832 | } | ||
1833 | |||
1834 | // reset every frame so that we only show highlight | ||
1835 | // when we get hover events on that frame | ||
1836 | mGotHover = FALSE; | ||
1837 | } | ||
1838 | |||
1839 | ///============================================================================ | ||
1840 | /// Class LLMenuGL | ||
1841 | ///============================================================================ | ||
1842 | |||
1843 | // Default constructor | ||
1844 | LLMenuGL::LLMenuGL( const LLString& name, const LLString& label, LLViewHandle parent_floater_handle ) | ||
1845 | : LLUICtrl( name, LLRect(), FALSE, NULL, NULL ), | ||
1846 | mBackgroundColor( sDefaultBackgroundColor ), | ||
1847 | mBgVisible( TRUE ), | ||
1848 | mParentMenuItem( NULL ), | ||
1849 | mLabel( label ), | ||
1850 | mDropShadowed( TRUE ), | ||
1851 | mHorizontalLayout( FALSE ), | ||
1852 | mKeepFixedSize( FALSE ), | ||
1853 | mLastMouseX(0), | ||
1854 | mLastMouseY(0), | ||
1855 | mMouseVelX(0), | ||
1856 | mMouseVelY(0), | ||
1857 | mTornOff(FALSE), | ||
1858 | mTearOffItem(NULL), | ||
1859 | mSpilloverBranch(NULL), | ||
1860 | mSpilloverMenu(NULL), | ||
1861 | mParentFloaterHandle(parent_floater_handle), | ||
1862 | mJumpKey(KEY_NONE) | ||
1863 | { | ||
1864 | mFadeTimer.stop(); | ||
1865 | setCanTearOff(TRUE, parent_floater_handle); | ||
1866 | setTabStop(FALSE); | ||
1867 | } | ||
1868 | |||
1869 | LLMenuGL::LLMenuGL( const LLString& label, LLViewHandle parent_floater_handle ) | ||
1870 | : LLUICtrl( label, LLRect(), FALSE, NULL, NULL ), | ||
1871 | mBackgroundColor( sDefaultBackgroundColor ), | ||
1872 | mBgVisible( TRUE ), | ||
1873 | mParentMenuItem( NULL ), | ||
1874 | mLabel( label ), | ||
1875 | mDropShadowed( TRUE ), | ||
1876 | mHorizontalLayout( FALSE ), | ||
1877 | mKeepFixedSize( FALSE ), | ||
1878 | mLastMouseX(0), | ||
1879 | mLastMouseY(0), | ||
1880 | mMouseVelX(0), | ||
1881 | mMouseVelY(0), | ||
1882 | mTornOff(FALSE), | ||
1883 | mTearOffItem(NULL), | ||
1884 | mSpilloverBranch(NULL), | ||
1885 | mSpilloverMenu(NULL), | ||
1886 | mParentFloaterHandle(parent_floater_handle), | ||
1887 | mJumpKey(KEY_NONE) | ||
1888 | { | ||
1889 | mFadeTimer.stop(); | ||
1890 | setCanTearOff(TRUE, parent_floater_handle); | ||
1891 | setTabStop(FALSE); | ||
1892 | } | ||
1893 | |||
1894 | // Destroys the object | ||
1895 | LLMenuGL::~LLMenuGL( void ) | ||
1896 | { | ||
1897 | // delete the branch, as it might not be in view hierarchy | ||
1898 | // leave the menu, because it is always in view hierarchy | ||
1899 | delete mSpilloverBranch; | ||
1900 | mJumpKeys.clear(); | ||
1901 | } | ||
1902 | |||
1903 | void LLMenuGL::setCanTearOff(BOOL tear_off, LLViewHandle parent_floater_handle ) | ||
1904 | { | ||
1905 | if (tear_off && mTearOffItem == NULL) | ||
1906 | { | ||
1907 | mTearOffItem = new LLMenuItemTearOffGL(parent_floater_handle); | ||
1908 | mItems.insert(mItems.begin(), mTearOffItem); | ||
1909 | addChildAtEnd(mTearOffItem); | ||
1910 | arrange(); | ||
1911 | } | ||
1912 | else if (!tear_off && mTearOffItem != NULL) | ||
1913 | { | ||
1914 | mItems.remove(mTearOffItem); | ||
1915 | removeChild(mTearOffItem); | ||
1916 | delete mTearOffItem; | ||
1917 | mTearOffItem = NULL; | ||
1918 | arrange(); | ||
1919 | } | ||
1920 | } | ||
1921 | |||
1922 | // virtual | ||
1923 | LLXMLNodePtr LLMenuGL::getXML(bool save_children) const | ||
1924 | { | ||
1925 | LLXMLNodePtr node = LLView::getXML(); | ||
1926 | |||
1927 | // Attributes | ||
1928 | |||
1929 | node->createChild("opaque", TRUE)->setBoolValue(mBgVisible); | ||
1930 | |||
1931 | node->createChild("drop_shadow", TRUE)->setBoolValue(mDropShadowed); | ||
1932 | |||
1933 | node->createChild("tear_off", TRUE)->setBoolValue((mTearOffItem != NULL)); | ||
1934 | |||
1935 | if (mBgVisible) | ||
1936 | { | ||
1937 | // TomY TODO: this should save out the color control name | ||
1938 | node->createChild("color", TRUE)->setFloatValue(4, mBackgroundColor.mV); | ||
1939 | } | ||
1940 | |||
1941 | // Contents | ||
1942 | item_list_t::const_iterator item_iter; | ||
1943 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
1944 | { | ||
1945 | LLView* child = (*item_iter); | ||
1946 | LLMenuItemGL* item = (LLMenuItemGL*)child; | ||
1947 | |||
1948 | LLXMLNodePtr child_node = item->getXML(); | ||
1949 | |||
1950 | node->addChild(child_node); | ||
1951 | } | ||
1952 | |||
1953 | return node; | ||
1954 | } | ||
1955 | |||
1956 | void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory) | ||
1957 | { | ||
1958 | if (child->hasName(LL_MENU_GL_TAG)) | ||
1959 | { | ||
1960 | // SUBMENU | ||
1961 | LLMenuGL *submenu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory); | ||
1962 | appendMenu(submenu); | ||
1963 | if (LLMenuGL::sDefaultMenuContainer != NULL) | ||
1964 | { | ||
1965 | submenu->updateParent(LLMenuGL::sDefaultMenuContainer); | ||
1966 | } | ||
1967 | else | ||
1968 | { | ||
1969 | submenu->updateParent(parent); | ||
1970 | } | ||
1971 | } | ||
1972 | else if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || | ||
1973 | child->hasName(LL_MENU_ITEM_CHECK_GL_TAG) || | ||
1974 | child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG)) | ||
1975 | { | ||
1976 | LLMenuItemGL *item = NULL; | ||
1977 | |||
1978 | LLString type; | ||
1979 | LLString item_name; | ||
1980 | LLString source_label; | ||
1981 | LLString item_label; | ||
1982 | KEY jump_key = KEY_NONE; | ||
1983 | |||
1984 | child->getAttributeString("type", type); | ||
1985 | child->getAttributeString("name", item_name); | ||
1986 | child->getAttributeString("label", source_label); | ||
1987 | |||
1988 | // parse jump key out of label | ||
1989 | typedef boost::tokenizer<boost::char_separator<char> > tokenizer; | ||
1990 | boost::char_separator<char> sep("_"); | ||
1991 | tokenizer tokens(source_label, sep); | ||
1992 | tokenizer::iterator token_iter; | ||
1993 | S32 token_count = 0; | ||
1994 | for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
1995 | { | ||
1996 | item_label += (*token_iter); | ||
1997 | if (token_count > 0) | ||
1998 | { | ||
1999 | jump_key = (*token_iter).c_str()[0]; | ||
2000 | } | ||
2001 | ++token_count; | ||
2002 | } | ||
2003 | |||
2004 | |||
2005 | if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG)) | ||
2006 | { | ||
2007 | appendSeparator(item_name); | ||
2008 | } | ||
2009 | else | ||
2010 | { | ||
2011 | // ITEM | ||
2012 | if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || | ||
2013 | child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) | ||
2014 | { | ||
2015 | MASK mask = 0; | ||
2016 | LLString shortcut; | ||
2017 | child->getAttributeString("shortcut", shortcut); | ||
2018 | if (shortcut.find("control") != shortcut.npos) | ||
2019 | { | ||
2020 | mask |= MASK_CONTROL; | ||
2021 | } | ||
2022 | if (shortcut.find("alt") != shortcut.npos) | ||
2023 | { | ||
2024 | mask |= MASK_ALT; | ||
2025 | } | ||
2026 | if (shortcut.find("shift") != shortcut.npos) | ||
2027 | { | ||
2028 | mask |= MASK_SHIFT; | ||
2029 | } | ||
2030 | S32 pipe_pos = shortcut.rfind("|"); | ||
2031 | LLString key_str = shortcut.substr(pipe_pos+1); | ||
2032 | |||
2033 | KEY key = KEY_NONE; | ||
2034 | LLKeyboard::keyFromString(key_str, &key); | ||
2035 | |||
2036 | LLMenuItemCallGL *new_item; | ||
2037 | LLXMLNodePtr call_child; | ||
2038 | |||
2039 | if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) | ||
2040 | { | ||
2041 | LLString control_name; | ||
2042 | child->getAttributeString("control_name", control_name); | ||
2043 | |||
2044 | new_item = new LLMenuItemCheckGL(item_name, item_label, 0, 0, control_name, parent, 0, key, mask); | ||
2045 | |||
2046 | for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) | ||
2047 | { | ||
2048 | if (call_child->hasName("on_check")) | ||
2049 | { | ||
2050 | LLString callback_name; | ||
2051 | LLString control_name = ""; | ||
2052 | if (call_child->hasAttribute("function")) | ||
2053 | { | ||
2054 | call_child->getAttributeString("function", callback_name); | ||
2055 | |||
2056 | control_name = callback_name; | ||
2057 | |||
2058 | LLString callback_data = item_name; | ||
2059 | if (call_child->hasAttribute("userdata")) | ||
2060 | { | ||
2061 | call_child->getAttributeString("userdata", callback_data); | ||
2062 | if (!callback_data.empty()) | ||
2063 | { | ||
2064 | control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); | ||
2065 | } | ||
2066 | } | ||
2067 | |||
2068 | LLSD userdata; | ||
2069 | userdata["control"] = control_name; | ||
2070 | userdata["data"] = callback_data; | ||
2071 | |||
2072 | LLSimpleListener* callback = parent->getListenerByName(callback_name); | ||
2073 | |||
2074 | if (!callback) continue; | ||
2075 | |||
2076 | new_item->addListener(callback, "on_build", userdata); | ||
2077 | } | ||
2078 | else if (call_child->hasAttribute("control")) | ||
2079 | { | ||
2080 | call_child->getAttributeString("control", control_name); | ||
2081 | } | ||
2082 | else | ||
2083 | { | ||
2084 | continue; | ||
2085 | } | ||
2086 | LLControlBase *control = parent->findControl(control_name); | ||
2087 | if (!control) | ||
2088 | { | ||
2089 | parent->addBoolControl(control_name, FALSE); | ||
2090 | } | ||
2091 | ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name, parent); | ||
2092 | } | ||
2093 | } | ||
2094 | } | ||
2095 | else | ||
2096 | { | ||
2097 | new_item = new LLMenuItemCallGL(item_name, item_label, 0, 0, 0, 0, key, mask); | ||
2098 | } | ||
2099 | |||
2100 | for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) | ||
2101 | { | ||
2102 | if (call_child->hasName("on_click")) | ||
2103 | { | ||
2104 | LLString callback_name; | ||
2105 | call_child->getAttributeString("function", callback_name); | ||
2106 | |||
2107 | LLString callback_data = item_name; | ||
2108 | if (call_child->hasAttribute("userdata")) | ||
2109 | { | ||
2110 | call_child->getAttributeString("userdata", callback_data); | ||
2111 | } | ||
2112 | |||
2113 | LLSimpleListener* callback = parent->getListenerByName(callback_name); | ||
2114 | |||
2115 | if (!callback) continue; | ||
2116 | |||
2117 | new_item->addListener(callback, "on_click", callback_data); | ||
2118 | } | ||
2119 | if (call_child->hasName("on_enable")) | ||
2120 | { | ||
2121 | LLString callback_name; | ||
2122 | LLString control_name = ""; | ||
2123 | if (call_child->hasAttribute("function")) | ||
2124 | { | ||
2125 | call_child->getAttributeString("function", callback_name); | ||
2126 | |||
2127 | control_name = callback_name; | ||
2128 | |||
2129 | LLString callback_data = ""; | ||
2130 | if (call_child->hasAttribute("userdata")) | ||
2131 | { | ||
2132 | call_child->getAttributeString("userdata", callback_data); | ||
2133 | if (!callback_data.empty()) | ||
2134 | { | ||
2135 | control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); | ||
2136 | } | ||
2137 | } | ||
2138 | |||
2139 | LLSD userdata; | ||
2140 | userdata["control"] = control_name; | ||
2141 | userdata["data"] = callback_data; | ||
2142 | |||
2143 | LLSimpleListener* callback = parent->getListenerByName(callback_name); | ||
2144 | |||
2145 | if (!callback) continue; | ||
2146 | |||
2147 | new_item->addListener(callback, "on_build", userdata); | ||
2148 | } | ||
2149 | else if (call_child->hasAttribute("control")) | ||
2150 | { | ||
2151 | call_child->getAttributeString("control", control_name); | ||
2152 | } | ||
2153 | else | ||
2154 | { | ||
2155 | continue; | ||
2156 | } | ||
2157 | new_item->setEnabledControl(control_name, parent); | ||
2158 | } | ||
2159 | if (call_child->hasName("on_visible")) | ||
2160 | { | ||
2161 | LLString callback_name; | ||
2162 | LLString control_name = ""; | ||
2163 | if (call_child->hasAttribute("function")) | ||
2164 | { | ||
2165 | call_child->getAttributeString("function", callback_name); | ||
2166 | |||
2167 | control_name = callback_name; | ||
2168 | |||
2169 | LLString callback_data = ""; | ||
2170 | if (call_child->hasAttribute("userdata")) | ||
2171 | { | ||
2172 | call_child->getAttributeString("userdata", callback_data); | ||
2173 | if (!callback_data.empty()) | ||
2174 | { | ||
2175 | control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); | ||
2176 | } | ||
2177 | } | ||
2178 | |||
2179 | LLSD userdata; | ||
2180 | userdata["control"] = control_name; | ||
2181 | userdata["data"] = callback_data; | ||
2182 | |||
2183 | LLSimpleListener* callback = parent->getListenerByName(callback_name); | ||
2184 | |||
2185 | if (!callback) continue; | ||
2186 | |||
2187 | new_item->addListener(callback, "on_build", userdata); | ||
2188 | } | ||
2189 | else if (call_child->hasAttribute("control")) | ||
2190 | { | ||
2191 | call_child->getAttributeString("control", control_name); | ||
2192 | } | ||
2193 | else | ||
2194 | { | ||
2195 | continue; | ||
2196 | } | ||
2197 | new_item->setVisibleControl(control_name, parent); | ||
2198 | } | ||
2199 | } | ||
2200 | item = new_item; | ||
2201 | item->setLabel(item_label); | ||
2202 | if (jump_key != KEY_NONE) | ||
2203 | item->setJumpKey(jump_key); | ||
2204 | } | ||
2205 | |||
2206 | if (item != NULL) | ||
2207 | { | ||
2208 | append(item); | ||
2209 | } | ||
2210 | } | ||
2211 | } | ||
2212 | } | ||
2213 | |||
2214 | // are we the childmost active menu and hence our jump keys should be enabled? | ||
2215 | // or are we a free-standing torn-off menu (which uses jump keys too) | ||
2216 | BOOL LLMenuGL::jumpKeysActive() | ||
2217 | { | ||
2218 | LLMenuItemGL* highlighted_item = getHighlightedItem(); | ||
2219 | BOOL active = getVisible() && getEnabled(); | ||
2220 | if (getTornOff()) | ||
2221 | { | ||
2222 | // activation of jump keys on torn off menus controlled by keyboard focus | ||
2223 | active = active && ((LLFloater*)getParent())->hasFocus(); | ||
2224 | |||
2225 | } | ||
2226 | else | ||
2227 | { | ||
2228 | // Are we the terminal active menu? | ||
2229 | // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus) | ||
2230 | // and we don't have a highlighted menu item pointing to an active sub-menu | ||
2231 | active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active... | ||
2232 | && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active | ||
2233 | } | ||
2234 | return active; | ||
2235 | } | ||
2236 | |||
2237 | BOOL LLMenuGL::isOpen() | ||
2238 | { | ||
2239 | if (getTornOff()) | ||
2240 | { | ||
2241 | LLMenuItemGL* itemp = getHighlightedItem(); | ||
2242 | // if we have an open sub-menu, then we are considered part of | ||
2243 | // the open menu chain even if we don't have focus | ||
2244 | if (itemp && itemp->isOpen()) | ||
2245 | { | ||
2246 | return TRUE; | ||
2247 | } | ||
2248 | // otherwise we are only active if we have keyboard focus | ||
2249 | return ((LLFloater*)getParent())->hasFocus(); | ||
2250 | } | ||
2251 | else | ||
2252 | { | ||
2253 | // normally, menus are hidden as soon as the user focuses | ||
2254 | // on another menu, so just use the visibility criterion | ||
2255 | return getVisible(); | ||
2256 | } | ||
2257 | } | ||
2258 | // static | ||
2259 | LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) | ||
2260 | { | ||
2261 | LLString name("menu"); | ||
2262 | node->getAttributeString("name", name); | ||
2263 | |||
2264 | LLString label = name; | ||
2265 | node->getAttributeString("label", label); | ||
2266 | |||
2267 | // parse jump key out of label | ||
2268 | LLString new_menu_label; | ||
2269 | |||
2270 | typedef boost::tokenizer<boost::char_separator<char> > tokenizer; | ||
2271 | boost::char_separator<char> sep("_"); | ||
2272 | tokenizer tokens(label, sep); | ||
2273 | tokenizer::iterator token_iter; | ||
2274 | |||
2275 | KEY jump_key = KEY_NONE; | ||
2276 | S32 token_count = 0; | ||
2277 | for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
2278 | { | ||
2279 | new_menu_label += (*token_iter); | ||
2280 | if (token_count > 0) | ||
2281 | { | ||
2282 | jump_key = (*token_iter).c_str()[0]; | ||
2283 | } | ||
2284 | ++token_count; | ||
2285 | } | ||
2286 | |||
2287 | BOOL opaque = FALSE; | ||
2288 | node->getAttributeBOOL("opaque", opaque); | ||
2289 | |||
2290 | LLMenuGL *menu = new LLMenuGL(name, new_menu_label); | ||
2291 | |||
2292 | menu->setJumpKey(jump_key); | ||
2293 | |||
2294 | BOOL tear_off = FALSE; | ||
2295 | node->getAttributeBOOL("tear_off", tear_off); | ||
2296 | menu->setCanTearOff(tear_off); | ||
2297 | |||
2298 | if (node->hasAttribute("drop_shadow")) | ||
2299 | { | ||
2300 | BOOL drop_shadow = FALSE; | ||
2301 | node->getAttributeBOOL("drop_shadow", drop_shadow); | ||
2302 | menu->setDropShadowed(drop_shadow); | ||
2303 | } | ||
2304 | |||
2305 | menu->setBackgroundVisible(opaque); | ||
2306 | LLColor4 color(0,0,0,1); | ||
2307 | if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color)) | ||
2308 | { | ||
2309 | menu->setBackgroundColor(color); | ||
2310 | } | ||
2311 | |||
2312 | BOOL create_jump_keys = FALSE; | ||
2313 | node->getAttributeBOOL("create_jump_keys", create_jump_keys); | ||
2314 | |||
2315 | LLXMLNodePtr child; | ||
2316 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | ||
2317 | { | ||
2318 | menu->parseChildXML(child, parent, factory); | ||
2319 | } | ||
2320 | |||
2321 | if (create_jump_keys) | ||
2322 | { | ||
2323 | menu->createJumpKeys(); | ||
2324 | } | ||
2325 | return menu; | ||
2326 | } | ||
2327 | |||
2328 | // control the color scheme | ||
2329 | void LLMenuGL::setDefaultBackgroundColor( const LLColor4& color ) | ||
2330 | { | ||
2331 | sDefaultBackgroundColor = color; | ||
2332 | } | ||
2333 | |||
2334 | void LLMenuGL::setBackgroundColor( const LLColor4& color ) | ||
2335 | { | ||
2336 | mBackgroundColor = color; | ||
2337 | } | ||
2338 | |||
2339 | // rearrange the child rects so they fit the shape of the menu. | ||
2340 | void LLMenuGL::arrange( void ) | ||
2341 | { | ||
2342 | // calculate the height & width, and set our rect based on that | ||
2343 | // information. | ||
2344 | LLRect initial_rect = mRect; | ||
2345 | |||
2346 | U32 width = 0, height = MENU_ITEM_PADDING; | ||
2347 | |||
2348 | cleanupSpilloverBranch(); | ||
2349 | |||
2350 | if( mItems.size() ) | ||
2351 | { | ||
2352 | U32 max_width = (getParent() != NULL) ? getParent()->getRect().getWidth() : U32_MAX; | ||
2353 | U32 max_height = (getParent() != NULL) ? getParent()->getRect().getHeight() : U32_MAX; | ||
2354 | // *FIX: create the item first and then ask for its dimensions? | ||
2355 | S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::sSansSerif->getWidth( "More" ); | ||
2356 | S32 spillover_item_height = llround(LLFontGL::sSansSerif->getLineHeight()) + MENU_ITEM_PADDING; | ||
2357 | |||
2358 | if (mHorizontalLayout) | ||
2359 | { | ||
2360 | item_list_t::iterator item_iter; | ||
2361 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2362 | { | ||
2363 | if ((*item_iter)->getVisible()) | ||
2364 | { | ||
2365 | if (!getTornOff() && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width) | ||
2366 | { | ||
2367 | // no room for any more items | ||
2368 | createSpilloverBranch(); | ||
2369 | |||
2370 | item_list_t::iterator spillover_iter; | ||
2371 | for (spillover_iter = item_iter; spillover_iter != mItems.end(); ++spillover_iter) | ||
2372 | { | ||
2373 | LLMenuItemGL* itemp = (*spillover_iter); | ||
2374 | removeChild(itemp); | ||
2375 | mSpilloverMenu->append(itemp); | ||
2376 | } | ||
2377 | mItems.erase(item_iter, mItems.end()); | ||
2378 | |||
2379 | mItems.push_back(mSpilloverBranch); | ||
2380 | addChild(mSpilloverBranch); | ||
2381 | height = llmax(height, mSpilloverBranch->getNominalHeight()); | ||
2382 | width += mSpilloverBranch->getNominalWidth(); | ||
2383 | |||
2384 | break; | ||
2385 | } | ||
2386 | else | ||
2387 | { | ||
2388 | // track our rect | ||
2389 | height = llmax(height, (*item_iter)->getNominalHeight()); | ||
2390 | width += (*item_iter)->getNominalWidth(); | ||
2391 | } | ||
2392 | } | ||
2393 | } | ||
2394 | } | ||
2395 | else | ||
2396 | { | ||
2397 | item_list_t::iterator item_iter; | ||
2398 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2399 | { | ||
2400 | if ((*item_iter)->getVisible()) | ||
2401 | { | ||
2402 | if (!getTornOff() && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height) | ||
2403 | { | ||
2404 | // no room for any more items | ||
2405 | createSpilloverBranch(); | ||
2406 | |||
2407 | item_list_t::iterator spillover_iter; | ||
2408 | for (spillover_iter= item_iter; spillover_iter != mItems.end(); ++spillover_iter) | ||
2409 | { | ||
2410 | LLMenuItemGL* itemp = (*spillover_iter); | ||
2411 | removeChild(itemp); | ||
2412 | mSpilloverMenu->append(itemp); | ||
2413 | } | ||
2414 | mItems.erase(item_iter, mItems.end()); | ||
2415 | mItems.push_back(mSpilloverBranch); | ||
2416 | addChild(mSpilloverBranch); | ||
2417 | height += mSpilloverBranch->getNominalHeight(); | ||
2418 | width = llmax( width, mSpilloverBranch->getNominalWidth() ); | ||
2419 | |||
2420 | break; | ||
2421 | } | ||
2422 | else | ||
2423 | { | ||
2424 | // track our rect | ||
2425 | height += (*item_iter)->getNominalHeight(); | ||
2426 | width = llmax( width, (*item_iter)->getNominalWidth() ); | ||
2427 | } | ||
2428 | } | ||
2429 | } | ||
2430 | } | ||
2431 | |||
2432 | mRect.mRight = mRect.mLeft + width; | ||
2433 | mRect.mTop = mRect.mBottom + height; | ||
2434 | |||
2435 | S32 cur_height = (S32)llmin(max_height, height); | ||
2436 | S32 cur_width = 0; | ||
2437 | item_list_t::iterator item_iter; | ||
2438 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2439 | { | ||
2440 | if ((*item_iter)->getVisible()) | ||
2441 | { | ||
2442 | // setup item rect to hold label | ||
2443 | LLRect rect; | ||
2444 | if (mHorizontalLayout) | ||
2445 | { | ||
2446 | rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height); | ||
2447 | cur_width += (*item_iter)->getNominalWidth(); | ||
2448 | } | ||
2449 | else | ||
2450 | { | ||
2451 | rect.setLeftTopAndSize( 0, cur_height, width, (*item_iter)->getNominalHeight()); | ||
2452 | cur_height -= (*item_iter)->getNominalHeight(); | ||
2453 | } | ||
2454 | (*item_iter)->setRect( rect ); | ||
2455 | (*item_iter)->buildDrawLabel(); | ||
2456 | } | ||
2457 | } | ||
2458 | } | ||
2459 | if (mKeepFixedSize) | ||
2460 | { | ||
2461 | reshape(initial_rect.getWidth(), initial_rect.getHeight()); | ||
2462 | } | ||
2463 | } | ||
2464 | |||
2465 | void LLMenuGL::createSpilloverBranch() | ||
2466 | { | ||
2467 | if (!mSpilloverBranch) | ||
2468 | { | ||
2469 | // should be NULL but delete anyway | ||
2470 | delete mSpilloverMenu; | ||
2471 | // technically, you can't tear off spillover menus, but we're passing the handle | ||
2472 | // along just to be safe | ||
2473 | mSpilloverMenu = new LLMenuGL("More", "More", mParentFloaterHandle); | ||
2474 | mSpilloverMenu->updateParent(getParent()); | ||
2475 | // Inherit colors | ||
2476 | mSpilloverMenu->setBackgroundColor( mBackgroundColor ); | ||
2477 | mSpilloverMenu->setCanTearOff(FALSE); | ||
2478 | |||
2479 | mSpilloverBranch = new LLMenuItemBranchGL("More", "More", mSpilloverMenu); | ||
2480 | mSpilloverBranch->setFontStyle(LLFontGL::ITALIC); | ||
2481 | } | ||
2482 | } | ||
2483 | |||
2484 | void LLMenuGL::cleanupSpilloverBranch() | ||
2485 | { | ||
2486 | if (mSpilloverBranch && mSpilloverBranch->getParent() == this) | ||
2487 | { | ||
2488 | // head-recursion to propagate items back up to root menu | ||
2489 | mSpilloverMenu->cleanupSpilloverBranch(); | ||
2490 | |||
2491 | removeChild(mSpilloverBranch); | ||
2492 | |||
2493 | item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), mSpilloverBranch); | ||
2494 | if (found_iter != mItems.end()) | ||
2495 | { | ||
2496 | mItems.erase(found_iter); | ||
2497 | } | ||
2498 | |||
2499 | // pop off spillover items | ||
2500 | while (mSpilloverMenu->getItemCount()) | ||
2501 | { | ||
2502 | LLMenuItemGL* itemp = mSpilloverMenu->getItem(0); | ||
2503 | mSpilloverMenu->removeChild(itemp); | ||
2504 | mSpilloverMenu->mItems.erase(mSpilloverMenu->mItems.begin()); | ||
2505 | // put them at the end of our own list | ||
2506 | mItems.push_back(itemp); | ||
2507 | addChild(itemp); | ||
2508 | } | ||
2509 | } | ||
2510 | } | ||
2511 | |||
2512 | void LLMenuGL::createJumpKeys() | ||
2513 | { | ||
2514 | mJumpKeys.clear(); | ||
2515 | |||
2516 | std::set<LLString> unique_words; | ||
2517 | std::set<LLString> shared_words; | ||
2518 | |||
2519 | item_list_t::iterator item_it; | ||
2520 | typedef boost::tokenizer<boost::char_separator<char> > tokenizer; | ||
2521 | boost::char_separator<char> sep(" "); | ||
2522 | |||
2523 | for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) | ||
2524 | { | ||
2525 | LLString uppercase_label = (*item_it)->getLabel(); | ||
2526 | LLString::toUpper(uppercase_label); | ||
2527 | |||
2528 | tokenizer tokens(uppercase_label, sep); | ||
2529 | tokenizer::iterator token_iter; | ||
2530 | for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
2531 | { | ||
2532 | if (unique_words.find(*token_iter) != unique_words.end()) | ||
2533 | { | ||
2534 | // this word exists in more than one menu instance | ||
2535 | shared_words.insert(*token_iter); | ||
2536 | } | ||
2537 | else | ||
2538 | { | ||
2539 | // we have a new word, keep track of it | ||
2540 | unique_words.insert(*token_iter); | ||
2541 | } | ||
2542 | } | ||
2543 | } | ||
2544 | |||
2545 | // pre-assign specified jump keys | ||
2546 | for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) | ||
2547 | { | ||
2548 | KEY jump_key = (*item_it)->getJumpKey(); | ||
2549 | if(jump_key != KEY_NONE) | ||
2550 | { | ||
2551 | if (mJumpKeys.find(jump_key) == mJumpKeys.end()) | ||
2552 | { | ||
2553 | mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it))); | ||
2554 | } | ||
2555 | else | ||
2556 | { | ||
2557 | // this key is already spoken for, | ||
2558 | // so we need to reassign it below | ||
2559 | (*item_it)->setJumpKey(KEY_NONE); | ||
2560 | } | ||
2561 | } | ||
2562 | } | ||
2563 | |||
2564 | for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) | ||
2565 | { | ||
2566 | // skip over items that already have assigned jump keys | ||
2567 | if ((*item_it)->getJumpKey() != KEY_NONE) | ||
2568 | { | ||
2569 | continue; | ||
2570 | } | ||
2571 | LLString uppercase_label = (*item_it)->getLabel(); | ||
2572 | LLString::toUpper(uppercase_label); | ||
2573 | |||
2574 | tokenizer tokens(uppercase_label, sep); | ||
2575 | tokenizer::iterator token_iter; | ||
2576 | |||
2577 | BOOL found_key = FALSE; | ||
2578 | for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
2579 | { | ||
2580 | LLString uppercase_word = *token_iter; | ||
2581 | |||
2582 | // this word is not shared with other menu entries... | ||
2583 | if (shared_words.find(*token_iter) == shared_words.end()) | ||
2584 | { | ||
2585 | S32 i; | ||
2586 | for(i = 0; i < (S32)uppercase_word.size(); i++) | ||
2587 | { | ||
2588 | char jump_key = uppercase_word[i]; | ||
2589 | |||
2590 | if (LLStringOps::isDigit(jump_key) || LLStringOps::isUpper(jump_key) && | ||
2591 | mJumpKeys.find(jump_key) == mJumpKeys.end()) | ||
2592 | { | ||
2593 | mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it))); | ||
2594 | (*item_it)->setJumpKey(jump_key); | ||
2595 | found_key = TRUE; | ||
2596 | break; | ||
2597 | } | ||
2598 | } | ||
2599 | } | ||
2600 | if (found_key) | ||
2601 | { | ||
2602 | break; | ||
2603 | } | ||
2604 | } | ||
2605 | } | ||
2606 | } | ||
2607 | |||
2608 | // remove all items on the menu | ||
2609 | void LLMenuGL::empty( void ) | ||
2610 | { | ||
2611 | mItems.clear(); | ||
2612 | |||
2613 | deleteAllChildren(); | ||
2614 | |||
2615 | } | ||
2616 | |||
2617 | // Adjust rectangle of the menu | ||
2618 | void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) | ||
2619 | { | ||
2620 | mRect.mLeft = left; | ||
2621 | mRect.mBottom = bottom; | ||
2622 | arrange(); | ||
2623 | } | ||
2624 | |||
2625 | BOOL LLMenuGL::handleJumpKey(KEY key) | ||
2626 | { | ||
2627 | // must perform case-insensitive comparison, so just switch to uppercase input key | ||
2628 | key = toupper(key); | ||
2629 | navigation_key_map_t::iterator found_it = mJumpKeys.find(key); | ||
2630 | if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) | ||
2631 | { | ||
2632 | // switch to keyboard navigation mode | ||
2633 | LLMenuGL::setKeyboardMode(TRUE); | ||
2634 | |||
2635 | // force highlight to close old menus and any open sub-menus | ||
2636 | |||
2637 | //clearHoverItem(); | ||
2638 | // force highlight to close old menus and open and sub-menus | ||
2639 | found_it->second->setHighlight(TRUE); | ||
2640 | found_it->second->doIt(); | ||
2641 | |||
2642 | } | ||
2643 | // if we are navigating the menus, we need to eat the keystroke | ||
2644 | // so rest of UI doesn't handle it | ||
2645 | return TRUE; | ||
2646 | } | ||
2647 | |||
2648 | |||
2649 | // Add the menu item to this menu. | ||
2650 | BOOL LLMenuGL::append( LLMenuItemGL* item ) | ||
2651 | { | ||
2652 | mItems.push_back( item ); | ||
2653 | addChild( item ); | ||
2654 | arrange(); | ||
2655 | return TRUE; | ||
2656 | } | ||
2657 | |||
2658 | // add a separator to this menu | ||
2659 | BOOL LLMenuGL::appendSeparator( const LLString &separator_name ) | ||
2660 | { | ||
2661 | LLMenuItemGL* separator = new LLMenuItemSeparatorGL(separator_name); | ||
2662 | return append( separator ); | ||
2663 | } | ||
2664 | |||
2665 | // add a menu - this will create a cascading menu | ||
2666 | BOOL LLMenuGL::appendMenu( LLMenuGL* menu ) | ||
2667 | { | ||
2668 | if( menu == this ) | ||
2669 | { | ||
2670 | llerrs << "** Attempt to attach menu to itself. This is certainly " | ||
2671 | << "a logic error." << llendl; | ||
2672 | } | ||
2673 | BOOL success = TRUE; | ||
2674 | |||
2675 | LLMenuItemBranchGL* branch = NULL; | ||
2676 | branch = new LLMenuItemBranchGL( menu->getName(), menu->getLabel(), menu ); | ||
2677 | branch->setJumpKey(menu->getJumpKey()); | ||
2678 | success &= append( branch ); | ||
2679 | |||
2680 | // Inherit colors | ||
2681 | menu->setBackgroundColor( mBackgroundColor ); | ||
2682 | |||
2683 | return success; | ||
2684 | } | ||
2685 | |||
2686 | void LLMenuGL::setEnabledSubMenus(BOOL enable) | ||
2687 | { | ||
2688 | setEnabled(enable); | ||
2689 | item_list_t::iterator item_iter; | ||
2690 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2691 | { | ||
2692 | (*item_iter)->setEnabledSubMenus( enable ); | ||
2693 | } | ||
2694 | } | ||
2695 | |||
2696 | // setItemEnabled() - pass the label and the enable flag for a menu | ||
2697 | // item. TRUE will make sure it's enabled, FALSE will disable it. | ||
2698 | void LLMenuGL::setItemEnabled( const LLString& name, BOOL enable ) | ||
2699 | { | ||
2700 | item_list_t::iterator item_iter; | ||
2701 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2702 | { | ||
2703 | if( (*item_iter)->getName() == name ) | ||
2704 | { | ||
2705 | (*item_iter)->setEnabled( enable ); | ||
2706 | (*item_iter)->setEnabledSubMenus( enable ); | ||
2707 | break; | ||
2708 | } | ||
2709 | } | ||
2710 | } | ||
2711 | |||
2712 | void LLMenuGL::setItemVisible( const LLString& name, BOOL visible ) | ||
2713 | { | ||
2714 | item_list_t::iterator item_iter; | ||
2715 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2716 | { | ||
2717 | if( (*item_iter)->getName() == name ) | ||
2718 | { | ||
2719 | (*item_iter)->setVisible( visible ); | ||
2720 | break; | ||
2721 | } | ||
2722 | } | ||
2723 | } | ||
2724 | |||
2725 | void LLMenuGL::setItemLastSelected(LLMenuItemGL* item) | ||
2726 | { | ||
2727 | if (getVisible()) | ||
2728 | { | ||
2729 | LLMenuHolderGL::setActivatedItem(item); | ||
2730 | } | ||
2731 | |||
2732 | // fix the checkmarks | ||
2733 | item->buildDrawLabel(); | ||
2734 | } | ||
2735 | |||
2736 | // Set whether drop shadowed | ||
2737 | void LLMenuGL::setDropShadowed( const BOOL shadowed ) | ||
2738 | { | ||
2739 | mDropShadowed = shadowed; | ||
2740 | } | ||
2741 | |||
2742 | void LLMenuGL::setTornOff(BOOL torn_off) | ||
2743 | { | ||
2744 | mTornOff = torn_off; | ||
2745 | } | ||
2746 | |||
2747 | U32 LLMenuGL::getItemCount() | ||
2748 | { | ||
2749 | return mItems.size(); | ||
2750 | } | ||
2751 | |||
2752 | LLMenuItemGL* LLMenuGL::getItem(S32 number) | ||
2753 | { | ||
2754 | if (number >= 0 && number < (S32)mItems.size()) | ||
2755 | { | ||
2756 | item_list_t::iterator item_iter; | ||
2757 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2758 | { | ||
2759 | if (number == 0) | ||
2760 | { | ||
2761 | return (*item_iter); | ||
2762 | } | ||
2763 | number--; | ||
2764 | } | ||
2765 | } | ||
2766 | return NULL; | ||
2767 | } | ||
2768 | |||
2769 | LLMenuItemGL* LLMenuGL::getHighlightedItem() | ||
2770 | { | ||
2771 | item_list_t::iterator item_iter; | ||
2772 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2773 | { | ||
2774 | if ((*item_iter)->getHighlight()) | ||
2775 | { | ||
2776 | return (*item_iter); | ||
2777 | } | ||
2778 | } | ||
2779 | return NULL; | ||
2780 | } | ||
2781 | |||
2782 | LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled) | ||
2783 | { | ||
2784 | // highlighting first item on a torn off menu is the | ||
2785 | // same as giving focus to it | ||
2786 | if (!cur_item && getTornOff()) | ||
2787 | { | ||
2788 | ((LLFloater*)getParent())->setFocus(TRUE); | ||
2789 | } | ||
2790 | |||
2791 | item_list_t::iterator cur_item_iter; | ||
2792 | for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter) | ||
2793 | { | ||
2794 | if( (*cur_item_iter) == cur_item) | ||
2795 | { | ||
2796 | break; | ||
2797 | } | ||
2798 | } | ||
2799 | |||
2800 | item_list_t::iterator next_item_iter; | ||
2801 | if (cur_item_iter == mItems.end()) | ||
2802 | { | ||
2803 | next_item_iter = mItems.begin(); | ||
2804 | } | ||
2805 | else | ||
2806 | { | ||
2807 | next_item_iter = cur_item_iter; | ||
2808 | next_item_iter++; | ||
2809 | if (next_item_iter == mItems.end()) | ||
2810 | { | ||
2811 | next_item_iter = mItems.begin(); | ||
2812 | } | ||
2813 | } | ||
2814 | |||
2815 | // when first highlighting a menu, skip over tear off menu item | ||
2816 | if (mTearOffItem && !cur_item) | ||
2817 | { | ||
2818 | // we know the first item is the tear off menu item | ||
2819 | cur_item_iter = mItems.begin(); | ||
2820 | next_item_iter++; | ||
2821 | if (next_item_iter == mItems.end()) | ||
2822 | { | ||
2823 | next_item_iter = mItems.begin(); | ||
2824 | } | ||
2825 | } | ||
2826 | |||
2827 | while(1) | ||
2828 | { | ||
2829 | // skip separators and disabled items | ||
2830 | if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getName() != SEPARATOR_NAME) | ||
2831 | { | ||
2832 | if (cur_item) | ||
2833 | { | ||
2834 | cur_item->setHighlight(FALSE); | ||
2835 | } | ||
2836 | (*next_item_iter)->setHighlight(TRUE); | ||
2837 | return (*next_item_iter); | ||
2838 | } | ||
2839 | |||
2840 | |||
2841 | if (!skip_disabled || next_item_iter == cur_item_iter) | ||
2842 | { | ||
2843 | break; | ||
2844 | } | ||
2845 | |||
2846 | next_item_iter++; | ||
2847 | if (next_item_iter == mItems.end()) | ||
2848 | { | ||
2849 | if (cur_item_iter == mItems.end()) | ||
2850 | { | ||
2851 | break; | ||
2852 | } | ||
2853 | next_item_iter = mItems.begin(); | ||
2854 | } | ||
2855 | } | ||
2856 | |||
2857 | return NULL; | ||
2858 | } | ||
2859 | |||
2860 | LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled) | ||
2861 | { | ||
2862 | // highlighting first item on a torn off menu is the | ||
2863 | // same as giving focus to it | ||
2864 | if (!cur_item && getTornOff()) | ||
2865 | { | ||
2866 | ((LLFloater*)getParent())->setFocus(TRUE); | ||
2867 | } | ||
2868 | |||
2869 | item_list_t::reverse_iterator cur_item_iter; | ||
2870 | for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter) | ||
2871 | { | ||
2872 | if( (*cur_item_iter) == cur_item) | ||
2873 | { | ||
2874 | break; | ||
2875 | } | ||
2876 | } | ||
2877 | |||
2878 | item_list_t::reverse_iterator prev_item_iter; | ||
2879 | if (cur_item_iter == mItems.rend()) | ||
2880 | { | ||
2881 | prev_item_iter = mItems.rbegin(); | ||
2882 | } | ||
2883 | else | ||
2884 | { | ||
2885 | prev_item_iter = cur_item_iter; | ||
2886 | prev_item_iter++; | ||
2887 | if (prev_item_iter == mItems.rend()) | ||
2888 | { | ||
2889 | prev_item_iter = mItems.rbegin(); | ||
2890 | } | ||
2891 | } | ||
2892 | |||
2893 | while(1) | ||
2894 | { | ||
2895 | // skip separators and disabled items | ||
2896 | if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getName() != SEPARATOR_NAME) | ||
2897 | { | ||
2898 | (*prev_item_iter)->setHighlight(TRUE); | ||
2899 | return (*prev_item_iter); | ||
2900 | } | ||
2901 | |||
2902 | if (!skip_disabled || prev_item_iter == cur_item_iter) | ||
2903 | { | ||
2904 | break; | ||
2905 | } | ||
2906 | |||
2907 | prev_item_iter++; | ||
2908 | if (prev_item_iter == mItems.rend()) | ||
2909 | { | ||
2910 | if (cur_item_iter == mItems.rend()) | ||
2911 | { | ||
2912 | break; | ||
2913 | } | ||
2914 | |||
2915 | prev_item_iter = mItems.rbegin(); | ||
2916 | } | ||
2917 | } | ||
2918 | |||
2919 | return NULL; | ||
2920 | } | ||
2921 | |||
2922 | void LLMenuGL::buildDrawLabels() | ||
2923 | { | ||
2924 | item_list_t::iterator item_iter; | ||
2925 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2926 | { | ||
2927 | (*item_iter)->buildDrawLabel(); | ||
2928 | } | ||
2929 | } | ||
2930 | |||
2931 | void LLMenuGL::updateParent(LLView* parentp) | ||
2932 | { | ||
2933 | if (getParent()) | ||
2934 | { | ||
2935 | getParent()->removeChild(this); | ||
2936 | } | ||
2937 | parentp->addChild(this); | ||
2938 | item_list_t::iterator item_iter; | ||
2939 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2940 | { | ||
2941 | (*item_iter)->updateBranchParent(parentp); | ||
2942 | } | ||
2943 | } | ||
2944 | |||
2945 | // LLView functionality | ||
2946 | BOOL LLMenuGL::handleKey( KEY key, MASK mask, BOOL called_from_parent ) | ||
2947 | { | ||
2948 | BOOL handled = FALSE; | ||
2949 | |||
2950 | // Pass down even if not visible | ||
2951 | if( mEnabled && called_from_parent ) | ||
2952 | { | ||
2953 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
2954 | { | ||
2955 | LLView* viewp = *child_it; | ||
2956 | if (viewp->handleKey(key, mask, TRUE)) | ||
2957 | { | ||
2958 | handled = TRUE; | ||
2959 | break; | ||
2960 | } | ||
2961 | } | ||
2962 | } | ||
2963 | |||
2964 | if( !handled ) | ||
2965 | { | ||
2966 | handled = handleKeyHere( key, mask, called_from_parent ); | ||
2967 | if (handled && LLView::sDebugKeys) | ||
2968 | { | ||
2969 | llinfos << "Key handled by " << getName() << llendl; | ||
2970 | } | ||
2971 | } | ||
2972 | |||
2973 | return handled; | ||
2974 | } | ||
2975 | |||
2976 | BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) | ||
2977 | { | ||
2978 | // Pass down even if not visible | ||
2979 | item_list_t::iterator item_iter; | ||
2980 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
2981 | { | ||
2982 | LLMenuItemGL* itemp = *item_iter; | ||
2983 | if (itemp->handleAcceleratorKey(key, mask)) | ||
2984 | { | ||
2985 | return TRUE; | ||
2986 | } | ||
2987 | } | ||
2988 | |||
2989 | return FALSE; | ||
2990 | } | ||
2991 | |||
2992 | BOOL LLMenuGL::handleUnicodeCharHere( llwchar uni_char, BOOL called_from_parent ) | ||
2993 | { | ||
2994 | if (jumpKeysActive()) | ||
2995 | { | ||
2996 | return handleJumpKey((KEY)uni_char); | ||
2997 | } | ||
2998 | return FALSE; | ||
2999 | } | ||
3000 | |||
3001 | BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) | ||
3002 | { | ||
3003 | // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU | ||
3004 | BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; | ||
3005 | S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; | ||
3006 | S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; | ||
3007 | LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y); | ||
3008 | mouse_dir.normVec(); | ||
3009 | LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); | ||
3010 | mouse_avg_dir.normVec(); | ||
3011 | F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f)); | ||
3012 | mMouseVelX = llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); | ||
3013 | mMouseVelY = llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); | ||
3014 | mLastMouseX = x; | ||
3015 | mLastMouseY = y; | ||
3016 | |||
3017 | // don't change menu focus unless mouse is moving or alt key is not held down | ||
3018 | if ((llabs(mMouseVelX) > 0 || | ||
3019 | llabs(mMouseVelY) > 0) && | ||
3020 | (!mHasSelection || | ||
3021 | //(mouse_delta_x == 0 && mouse_delta_y == 0) || | ||
3022 | (mMouseVelX < 0) || | ||
3023 | llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU)) | ||
3024 | { | ||
3025 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
3026 | { | ||
3027 | LLView* viewp = *child_it; | ||
3028 | S32 local_x = x - viewp->getRect().mLeft; | ||
3029 | S32 local_y = y - viewp->getRect().mBottom; | ||
3030 | if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) | ||
3031 | { | ||
3032 | // moving mouse always highlights new item | ||
3033 | if (mouse_delta_x != 0 || mouse_delta_y != 0) | ||
3034 | { | ||
3035 | ((LLMenuItemGL*)viewp)->setHighlight(FALSE); | ||
3036 | } | ||
3037 | } | ||
3038 | } | ||
3039 | |||
3040 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
3041 | { | ||
3042 | LLView* viewp = *child_it; | ||
3043 | S32 local_x = x - viewp->getRect().mLeft; | ||
3044 | S32 local_y = y - viewp->getRect().mBottom; | ||
3045 | //RN: always call handleHover to track mGotHover status | ||
3046 | // but only set highlight when mouse is moving | ||
3047 | if( viewp->getVisible() && | ||
3048 | //RN: allow disabled items to be highlighted to preserve "active" menus when | ||
3049 | // moving mouse through them | ||
3050 | //viewp->getEnabled() && | ||
3051 | viewp->pointInView(local_x, local_y) && | ||
3052 | viewp->handleHover(local_x, local_y, mask)) | ||
3053 | { | ||
3054 | // moving mouse always highlights new item | ||
3055 | if (mouse_delta_x != 0 || mouse_delta_y != 0) | ||
3056 | { | ||
3057 | ((LLMenuItemGL*)viewp)->setHighlight(TRUE); | ||
3058 | LLMenuGL::setKeyboardMode(FALSE); | ||
3059 | } | ||
3060 | mHasSelection = TRUE; | ||
3061 | } | ||
3062 | } | ||
3063 | } | ||
3064 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
3065 | return TRUE; | ||
3066 | } | ||
3067 | |||
3068 | void LLMenuGL::draw( void ) | ||
3069 | { | ||
3070 | if (mDropShadowed && !mTornOff) | ||
3071 | { | ||
3072 | gl_drop_shadow(0, mRect.getHeight(), mRect.getWidth(), 0, | ||
3073 | LLUI::sColorsGroup->getColor("ColorDropShadow"), | ||
3074 | LLUI::sConfigGroup->getS32("DropShadowFloater") ); | ||
3075 | } | ||
3076 | |||
3077 | LLColor4 bg_color = mBackgroundColor; | ||
3078 | |||
3079 | if( mBgVisible ) | ||
3080 | { | ||
3081 | gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0, mBackgroundColor ); | ||
3082 | } | ||
3083 | LLView::draw(); | ||
3084 | } | ||
3085 | |||
3086 | void LLMenuGL::drawBackground(LLMenuItemGL* itemp, LLColor4& color) | ||
3087 | { | ||
3088 | glColor4fv( color.mV ); | ||
3089 | LLRect item_rect = itemp->getRect(); | ||
3090 | gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0); | ||
3091 | } | ||
3092 | |||
3093 | void LLMenuGL::setVisible(BOOL visible) | ||
3094 | { | ||
3095 | if (visible != getVisible()) | ||
3096 | { | ||
3097 | if (!visible) | ||
3098 | { | ||
3099 | mFadeTimer.start(); | ||
3100 | clearHoverItem(); | ||
3101 | // reset last known mouse coordinates so | ||
3102 | // we don't spoof a mouse move next time we're opened | ||
3103 | mLastMouseX = 0; | ||
3104 | mLastMouseY = 0; | ||
3105 | } | ||
3106 | else | ||
3107 | { | ||
3108 | mHasSelection = FALSE; | ||
3109 | mFadeTimer.stop(); | ||
3110 | } | ||
3111 | |||
3112 | //gViewerWindow->finishFastFrame(); | ||
3113 | LLView::setVisible(visible); | ||
3114 | } | ||
3115 | } | ||
3116 | |||
3117 | LLMenuGL* LLMenuGL::getChildMenuByName(const LLString& name, BOOL recurse) const | ||
3118 | { | ||
3119 | LLView* view = getChildByName(name, recurse); | ||
3120 | if (view) | ||
3121 | { | ||
3122 | if (view->getWidgetType() == WIDGET_TYPE_MENU_ITEM_BRANCH) | ||
3123 | { | ||
3124 | LLMenuItemBranchGL *branch = (LLMenuItemBranchGL *)view; | ||
3125 | return branch->getBranch(); | ||
3126 | } | ||
3127 | if (view->getWidgetType() == WIDGET_TYPE_MENU || view->getWidgetType() == WIDGET_TYPE_PIE_MENU) | ||
3128 | { | ||
3129 | return (LLMenuGL*)view; | ||
3130 | } | ||
3131 | } | ||
3132 | llwarns << "Child Menu " << name << " not found in menu " << mName << llendl; | ||
3133 | return NULL; | ||
3134 | } | ||
3135 | |||
3136 | BOOL LLMenuGL::clearHoverItem() | ||
3137 | { | ||
3138 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
3139 | { | ||
3140 | LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; | ||
3141 | if (itemp->getHighlight()) | ||
3142 | { | ||
3143 | itemp->setHighlight(FALSE); | ||
3144 | return TRUE; | ||
3145 | } | ||
3146 | } | ||
3147 | return FALSE; | ||
3148 | } | ||
3149 | |||
3150 | void hide_top_view( LLView* view ) | ||
3151 | { | ||
3152 | if( view ) view->setVisible( FALSE ); | ||
3153 | } | ||
3154 | |||
3155 | |||
3156 | // static | ||
3157 | void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) | ||
3158 | { | ||
3159 | const S32 HPAD = 2; | ||
3160 | LLRect rect = menu->getRect(); | ||
3161 | //LLView* cur_view = spawning_view; | ||
3162 | S32 left = x + HPAD; | ||
3163 | S32 top = y; | ||
3164 | spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); | ||
3165 | rect.setLeftTopAndSize( left, top, | ||
3166 | rect.getWidth(), rect.getHeight() ); | ||
3167 | |||
3168 | |||
3169 | //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight()); | ||
3170 | menu->setRect( rect ); | ||
3171 | |||
3172 | S32 bottom; | ||
3173 | left = rect.mLeft; | ||
3174 | bottom = rect.mBottom; | ||
3175 | //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom, | ||
3176 | // &left, &bottom ); | ||
3177 | S32 delta_x = 0; | ||
3178 | S32 delta_y = 0; | ||
3179 | if( bottom < 0 ) | ||
3180 | { | ||
3181 | delta_y = -bottom; | ||
3182 | } | ||
3183 | |||
3184 | S32 parent_width = menu->getParent()->getRect().getWidth(); | ||
3185 | if( left > parent_width - rect.getWidth() ) | ||
3186 | { | ||
3187 | // At this point, we need to move the context menu to the | ||
3188 | // other side of the mouse. | ||
3189 | //delta_x = (window_width - rect.getWidth()) - x; | ||
3190 | delta_x = -(rect.getWidth() + 2 * HPAD); | ||
3191 | } | ||
3192 | menu->translate( delta_x, delta_y ); | ||
3193 | menu->setVisible( TRUE ); | ||
3194 | } | ||
3195 | |||
3196 | //----------------------------------------------------------------------------- | ||
3197 | // class LLPieMenuBranch | ||
3198 | // A branch to another pie menu | ||
3199 | //----------------------------------------------------------------------------- | ||
3200 | class LLPieMenuBranch : public LLMenuItemGL | ||
3201 | { | ||
3202 | public: | ||
3203 | LLPieMenuBranch(const LLString& name, const LLString& label, LLPieMenu* branch, | ||
3204 | enabled_callback ecb, void* user_data); | ||
3205 | |||
3206 | virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_PIE_MENU_BRANCH; } | ||
3207 | virtual LLString getWidgetTag() const { return LL_PIE_MENU_BRANCH_TAG; } | ||
3208 | |||
3209 | // called to rebuild the draw label | ||
3210 | virtual void buildDrawLabel( void ); | ||
3211 | |||
3212 | // doIt() - do the primary funcationality of the menu item. | ||
3213 | virtual void doIt( void ); | ||
3214 | |||
3215 | LLPieMenu* getBranch() { return mBranch; } | ||
3216 | |||
3217 | protected: | ||
3218 | LLPieMenu* mBranch; | ||
3219 | enabled_callback mEnabledCallback; | ||
3220 | void* mUserData; | ||
3221 | }; | ||
3222 | |||
3223 | LLPieMenuBranch::LLPieMenuBranch(const LLString& name, | ||
3224 | const LLString& label, | ||
3225 | LLPieMenu* branch, | ||
3226 | enabled_callback ecb, | ||
3227 | void* user_data) | ||
3228 | : LLMenuItemGL( name, label, KEY_NONE, MASK_NONE ), | ||
3229 | mBranch( branch ), | ||
3230 | mEnabledCallback( ecb ), | ||
3231 | mUserData(user_data) | ||
3232 | { | ||
3233 | mBranch->hide(FALSE); | ||
3234 | mBranch->setParentMenuItem(this); | ||
3235 | } | ||
3236 | |||
3237 | // called to rebuild the draw label | ||
3238 | void LLPieMenuBranch::buildDrawLabel( void ) | ||
3239 | { | ||
3240 | if(mEnabledCallback) | ||
3241 | { | ||
3242 | setEnabled(mEnabledCallback(mUserData)); | ||
3243 | mDrawTextDisabled = FALSE; | ||
3244 | } | ||
3245 | else | ||
3246 | { | ||
3247 | // default enablement is this -- if any of the subitems are | ||
3248 | // enabled, this item is enabled. JC | ||
3249 | U32 sub_count = mBranch->getItemCount(); | ||
3250 | U32 i; | ||
3251 | BOOL any_enabled = FALSE; | ||
3252 | for (i = 0; i < sub_count; i++) | ||
3253 | { | ||
3254 | LLMenuItemGL* item = mBranch->getItem(i); | ||
3255 | item->buildDrawLabel(); | ||
3256 | if (item->getEnabled() && !item->getDrawTextDisabled() ) | ||
3257 | { | ||
3258 | any_enabled = TRUE; | ||
3259 | break; | ||
3260 | } | ||
3261 | } | ||
3262 | mDrawTextDisabled = !any_enabled; | ||
3263 | setEnabled(TRUE); | ||
3264 | } | ||
3265 | |||
3266 | mDrawAccelLabel.clear(); | ||
3267 | LLString st = mDrawAccelLabel; | ||
3268 | appendAcceleratorString( st ); | ||
3269 | mDrawAccelLabel = st; | ||
3270 | |||
3271 | // No special branch suffix | ||
3272 | mDrawBranchLabel.clear(); | ||
3273 | } | ||
3274 | |||
3275 | // doIt() - do the primary funcationality of the menu item. | ||
3276 | void LLPieMenuBranch::doIt( void ) | ||
3277 | { | ||
3278 | LLPieMenu *parent = (LLPieMenu *)getParent(); | ||
3279 | |||
3280 | LLRect rect = parent->getRect(); | ||
3281 | S32 center_x; | ||
3282 | S32 center_y; | ||
3283 | parent->localPointToScreen(rect.getWidth() / 2, rect.getHeight() / 2, ¢er_x, ¢er_y); | ||
3284 | |||
3285 | parent->hide(TRUE); | ||
3286 | mBranch->show( center_x, center_y, FALSE ); | ||
3287 | } | ||
3288 | |||
3289 | //----------------------------------------------------------------------------- | ||
3290 | // class LLPieMenu | ||
3291 | // A circular menu of items, icons, etc. | ||
3292 | //----------------------------------------------------------------------------- | ||
3293 | LLPieMenu::LLPieMenu(const LLString& name, const LLString& label) | ||
3294 | : LLMenuGL(name, label), | ||
3295 | mFirstMouseDown(FALSE), | ||
3296 | mUseInfiniteRadius(FALSE), | ||
3297 | mHoverItem(NULL), | ||
3298 | mHoverThisFrame(FALSE), | ||
3299 | mOuterRingAlpha(1.f), | ||
3300 | mCurRadius(0.f), | ||
3301 | mRightMouseDown(FALSE) | ||
3302 | { | ||
3303 | LLMenuGL::setVisible(FALSE); | ||
3304 | setCanTearOff(FALSE); | ||
3305 | } | ||
3306 | |||
3307 | LLPieMenu::LLPieMenu(const LLString& name) | ||
3308 | : LLMenuGL(name, name), | ||
3309 | mFirstMouseDown(FALSE), | ||
3310 | mUseInfiniteRadius(FALSE), | ||
3311 | mHoverItem(NULL), | ||
3312 | mHoverThisFrame(FALSE), | ||
3313 | mOuterRingAlpha(1.f), | ||
3314 | mCurRadius(0.f), | ||
3315 | mRightMouseDown(FALSE) | ||
3316 | { | ||
3317 | LLMenuGL::setVisible(FALSE); | ||
3318 | setCanTearOff(FALSE); | ||
3319 | } | ||
3320 | |||
3321 | // virtual | ||
3322 | LLPieMenu::~LLPieMenu() | ||
3323 | { } | ||
3324 | |||
3325 | |||
3326 | EWidgetType LLPieMenu::getWidgetType() const | ||
3327 | { | ||
3328 | return WIDGET_TYPE_PIE_MENU; | ||
3329 | } | ||
3330 | |||
3331 | LLString LLPieMenu::getWidgetTag() const | ||
3332 | { | ||
3333 | return LL_PIE_MENU_TAG; | ||
3334 | } | ||
3335 | |||
3336 | void LLPieMenu::initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *factory) | ||
3337 | { | ||
3338 | LLXMLNodePtr child; | ||
3339 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | ||
3340 | { | ||
3341 | if (child->hasName(LL_PIE_MENU_TAG)) | ||
3342 | { | ||
3343 | // SUBMENU | ||
3344 | LLString name("menu"); | ||
3345 | child->getAttributeString("name", name); | ||
3346 | LLString label(name); | ||
3347 | child->getAttributeString("label", label); | ||
3348 | |||
3349 | LLPieMenu *submenu = new LLPieMenu(name, label); | ||
3350 | appendMenu(submenu); | ||
3351 | submenu->initXML(child, context, factory); | ||
3352 | } | ||
3353 | else | ||
3354 | { | ||
3355 | parseChildXML(child, context, factory); | ||
3356 | } | ||
3357 | } | ||
3358 | } | ||
3359 | |||
3360 | // virtual | ||
3361 | void LLPieMenu::setVisible(BOOL visible) | ||
3362 | { | ||
3363 | if (!visible) | ||
3364 | { | ||
3365 | hide(FALSE); | ||
3366 | } | ||
3367 | } | ||
3368 | |||
3369 | BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask ) | ||
3370 | { | ||
3371 | // This is mostly copied from the llview class, but it continues | ||
3372 | // the hover handle code after a hover handler has been found. | ||
3373 | BOOL handled = FALSE; | ||
3374 | |||
3375 | // If we got a hover event, we've already moved the cursor | ||
3376 | // for any menu shifts, so subsequent mouseup messages will be in the | ||
3377 | // correct position. No need to correct them. | ||
3378 | //mShiftHoriz = 0; | ||
3379 | //mShiftVert = 0; | ||
3380 | |||
3381 | // release mouse capture after short period of visibility if we're using a finite boundary | ||
3382 | // so that right click outside of boundary will trigger new pie menu | ||
3383 | if (gFocusMgr.getMouseCapture() == this && | ||
3384 | !mRightMouseDown && | ||
3385 | mShrinkBorderTimer.getStarted() && | ||
3386 | mShrinkBorderTimer.getElapsedTimeF32() >= PIE_SHRINK_TIME) | ||
3387 | { | ||
3388 | gFocusMgr.setMouseCapture(NULL, NULL); | ||
3389 | mUseInfiniteRadius = FALSE; | ||
3390 | } | ||
3391 | |||
3392 | LLMenuItemGL *item = pieItemFromXY( x, y ); | ||
3393 | |||
3394 | if (item && item->getEnabled()) | ||
3395 | { | ||
3396 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
3397 | lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; | ||
3398 | handled = TRUE; | ||
3399 | |||
3400 | if (item != mHoverItem) | ||
3401 | { | ||
3402 | if (mHoverItem) | ||
3403 | { | ||
3404 | mHoverItem->setHighlight( FALSE ); | ||
3405 | } | ||
3406 | mHoverItem = item; | ||
3407 | mHoverItem->setHighlight( TRUE ); | ||
3408 | |||
3409 | switch(pieItemIndexFromXY(x, y)) | ||
3410 | { | ||
3411 | case 0: | ||
3412 | make_ui_sound("UISndPieMenuSliceHighlight0"); | ||
3413 | break; | ||
3414 | case 1: | ||
3415 | make_ui_sound("UISndPieMenuSliceHighlight1"); | ||
3416 | break; | ||
3417 | case 2: | ||
3418 | make_ui_sound("UISndPieMenuSliceHighlight2"); | ||
3419 | break; | ||
3420 | case 3: | ||
3421 | make_ui_sound("UISndPieMenuSliceHighlight3"); | ||
3422 | break; | ||
3423 | case 4: | ||
3424 | make_ui_sound("UISndPieMenuSliceHighlight4"); | ||
3425 | break; | ||
3426 | case 5: | ||
3427 | make_ui_sound("UISndPieMenuSliceHighlight5"); | ||
3428 | break; | ||
3429 | case 6: | ||
3430 | make_ui_sound("UISndPieMenuSliceHighlight6"); | ||
3431 | break; | ||
3432 | case 7: | ||
3433 | make_ui_sound("UISndPieMenuSliceHighlight7"); | ||
3434 | break; | ||
3435 | default: | ||
3436 | make_ui_sound("UISndPieMenuSliceHighlight0"); | ||
3437 | break; | ||
3438 | } | ||
3439 | } | ||
3440 | } | ||
3441 | else | ||
3442 | { | ||
3443 | // clear out our selection | ||
3444 | if (mHoverItem) | ||
3445 | { | ||
3446 | mHoverItem->setHighlight(FALSE); | ||
3447 | mHoverItem = NULL; | ||
3448 | } | ||
3449 | } | ||
3450 | |||
3451 | if( !handled && pointInView( x, y ) ) | ||
3452 | { | ||
3453 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
3454 | lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; | ||
3455 | handled = TRUE; | ||
3456 | } | ||
3457 | |||
3458 | mHoverThisFrame = TRUE; | ||
3459 | |||
3460 | return handled; | ||
3461 | } | ||
3462 | |||
3463 | BOOL LLPieMenu::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
3464 | { | ||
3465 | BOOL handled = FALSE; | ||
3466 | // The click was somewhere within our rectangle | ||
3467 | LLMenuItemGL *item = pieItemFromXY( x, y ); | ||
3468 | |||
3469 | if (item) | ||
3470 | { | ||
3471 | // lie to the item about where the click happened | ||
3472 | // to make sure it's within the item's rectangle | ||
3473 | handled = item->handleMouseDown( 0, 0, mask ); | ||
3474 | } | ||
3475 | |||
3476 | // always handle mouse down as mouse up will close open menus | ||
3477 | return handled; | ||
3478 | } | ||
3479 | |||
3480 | BOOL LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) | ||
3481 | { | ||
3482 | BOOL handled = FALSE; | ||
3483 | |||
3484 | mRightMouseDown = TRUE; | ||
3485 | |||
3486 | // The click was somewhere within our rectangle | ||
3487 | LLMenuItemGL *item = pieItemFromXY( x, y ); | ||
3488 | S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX(); | ||
3489 | S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY(); | ||
3490 | BOOL clicked_in_pie = ((delta_x * delta_x) + (delta_y * delta_y) < mCurRadius*mCurRadius) || mUseInfiniteRadius; | ||
3491 | |||
3492 | // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie | ||
3493 | if (clicked_in_pie) | ||
3494 | { | ||
3495 | // capture mouse cursor as if on initial menu show | ||
3496 | gFocusMgr.setMouseCapture(this, NULL); | ||
3497 | mShrinkBorderTimer.stop(); | ||
3498 | mUseInfiniteRadius = TRUE; | ||
3499 | handled = TRUE; | ||
3500 | } | ||
3501 | |||
3502 | if (item) | ||
3503 | { | ||
3504 | // lie to the item about where the click happened | ||
3505 | // to make sure it's within the item's rectangle | ||
3506 | if (item->handleMouseDown( 0, 0, mask )) | ||
3507 | { | ||
3508 | handled = TRUE; | ||
3509 | } | ||
3510 | } | ||
3511 | |||
3512 | return handled; | ||
3513 | } | ||
3514 | |||
3515 | BOOL LLPieMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) | ||
3516 | { | ||
3517 | // release mouse capture when right mouse button released, and we're past the shrink time | ||
3518 | if (mShrinkBorderTimer.getStarted() && | ||
3519 | mShrinkBorderTimer.getElapsedTimeF32() > PIE_SHRINK_TIME) | ||
3520 | { | ||
3521 | mUseInfiniteRadius = FALSE; | ||
3522 | gFocusMgr.setMouseCapture(NULL, NULL); | ||
3523 | } | ||
3524 | |||
3525 | BOOL result = handleMouseUp( x, y, mask ); | ||
3526 | mRightMouseDown = FALSE; | ||
3527 | |||
3528 | return result; | ||
3529 | } | ||
3530 | |||
3531 | BOOL LLPieMenu::handleMouseUp( S32 x, S32 y, MASK mask ) | ||
3532 | { | ||
3533 | BOOL handled = FALSE; | ||
3534 | |||
3535 | // The click was somewhere within our rectangle | ||
3536 | LLMenuItemGL *item = pieItemFromXY( x, y ); | ||
3537 | |||
3538 | if (item) | ||
3539 | { | ||
3540 | // lie to the item about where the click happened | ||
3541 | // to make sure it's within the item's rectangle | ||
3542 | if (item->getEnabled()) | ||
3543 | { | ||
3544 | handled = item->handleMouseUp( 0, 0, mask ); | ||
3545 | hide(TRUE); | ||
3546 | } | ||
3547 | } | ||
3548 | |||
3549 | if (handled) | ||
3550 | { | ||
3551 | make_ui_sound("UISndClickRelease"); | ||
3552 | } | ||
3553 | |||
3554 | if (!handled && !mUseInfiniteRadius) | ||
3555 | { | ||
3556 | // call hidemenus to make sure transient selections get cleared | ||
3557 | ((LLMenuHolderGL*)getParent())->hideMenus(); | ||
3558 | } | ||
3559 | |||
3560 | if (mFirstMouseDown) | ||
3561 | { | ||
3562 | make_ui_sound("UISndPieMenuAppear"); | ||
3563 | mFirstMouseDown = FALSE; | ||
3564 | } | ||
3565 | |||
3566 | // *FIX: is this necessary? | ||
3567 | if (!mShrinkBorderTimer.getStarted()) | ||
3568 | { | ||
3569 | mShrinkBorderTimer.start(); | ||
3570 | } | ||
3571 | |||
3572 | return handled; | ||
3573 | } | ||
3574 | |||
3575 | |||
3576 | // virtual | ||
3577 | void LLPieMenu::draw() | ||
3578 | { | ||
3579 | // clear hover if mouse moved away | ||
3580 | if (!mHoverThisFrame && mHoverItem) | ||
3581 | { | ||
3582 | mHoverItem->setHighlight(FALSE); | ||
3583 | mHoverItem = NULL; | ||
3584 | } | ||
3585 | |||
3586 | F32 width = (F32) mRect.getWidth(); | ||
3587 | F32 height = (F32) mRect.getHeight(); | ||
3588 | mCurRadius = PIE_SCALE_FACTOR * llmax( width/2, height/2 ); | ||
3589 | |||
3590 | mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f; | ||
3591 | if (mShrinkBorderTimer.getStarted()) | ||
3592 | { | ||
3593 | mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 0.f, 1.f); | ||
3594 | mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR); | ||
3595 | } | ||
3596 | |||
3597 | // correct for non-square pixels | ||
3598 | F32 center_x = width/2; | ||
3599 | F32 center_y = height/2; | ||
3600 | S32 steps = 100; | ||
3601 | |||
3602 | glPushMatrix(); | ||
3603 | { | ||
3604 | glTranslatef(center_x, center_y, 0.f); | ||
3605 | |||
3606 | F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth"); | ||
3607 | LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor"); | ||
3608 | LLColor4 bg_color = LLUI::sColorsGroup->getColor("PieMenuBgColor"); | ||
3609 | LLColor4 selected_color = LLUI::sColorsGroup->getColor("PieMenuSelectedColor"); | ||
3610 | |||
3611 | // main body | ||
3612 | LLColor4 outer_color = bg_color; | ||
3613 | outer_color.mV[VALPHA] *= mOuterRingAlpha; | ||
3614 | gl_washer_2d( mCurRadius, (F32) PIE_CENTER_SIZE, steps, bg_color, outer_color ); | ||
3615 | |||
3616 | // selected wedge | ||
3617 | item_list_t::iterator item_iter; | ||
3618 | S32 i = 0; | ||
3619 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
3620 | { | ||
3621 | if ((*item_iter)->getHighlight()) | ||
3622 | { | ||
3623 | F32 arc_size = F_PI * 0.25f; | ||
3624 | |||
3625 | F32 start_radians = (i * arc_size) - (arc_size * 0.5f); | ||
3626 | F32 end_radians = start_radians + arc_size; | ||
3627 | |||
3628 | LLColor4 outer_color = selected_color; | ||
3629 | outer_color.mV[VALPHA] *= mOuterRingAlpha; | ||
3630 | gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, selected_color, outer_color ); | ||
3631 | } | ||
3632 | i++; | ||
3633 | } | ||
3634 | |||
3635 | LLUI::setLineWidth( line_width ); | ||
3636 | |||
3637 | // inner lines | ||
3638 | outer_color = line_color; | ||
3639 | outer_color.mV[VALPHA] *= mOuterRingAlpha; | ||
3640 | gl_washer_spokes_2d( mCurRadius, (F32)PIE_CENTER_SIZE, 8, line_color, outer_color ); | ||
3641 | |||
3642 | // inner circle | ||
3643 | glColor4fv( line_color.mV ); | ||
3644 | gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE ); | ||
3645 | |||
3646 | // outer circle | ||
3647 | glColor4fv( outer_color.mV ); | ||
3648 | gl_circle_2d( 0, 0, mCurRadius, steps, FALSE ); | ||
3649 | |||
3650 | LLUI::setLineWidth(1.0f); | ||
3651 | } | ||
3652 | glPopMatrix(); | ||
3653 | |||
3654 | mHoverThisFrame = FALSE; | ||
3655 | |||
3656 | LLView::draw(); | ||
3657 | } | ||
3658 | |||
3659 | void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color) | ||
3660 | { | ||
3661 | F32 width = (F32) mRect.getWidth(); | ||
3662 | F32 height = (F32) mRect.getHeight(); | ||
3663 | F32 center_x = width/2; | ||
3664 | F32 center_y = height/2; | ||
3665 | S32 steps = 100; | ||
3666 | |||
3667 | glColor4fv( color.mV ); | ||
3668 | glPushMatrix(); | ||
3669 | { | ||
3670 | glTranslatef(center_x - itemp->getRect().mLeft, center_y - itemp->getRect().mBottom, 0.f); | ||
3671 | |||
3672 | item_list_t::iterator item_iter; | ||
3673 | S32 i = 0; | ||
3674 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
3675 | { | ||
3676 | if ((*item_iter) == itemp) | ||
3677 | { | ||
3678 | F32 arc_size = F_PI * 0.25f; | ||
3679 | |||
3680 | F32 start_radians = (i * arc_size) - (arc_size * 0.5f); | ||
3681 | F32 end_radians = start_radians + arc_size; | ||
3682 | |||
3683 | LLColor4 outer_color = color; | ||
3684 | outer_color.mV[VALPHA] *= mOuterRingAlpha; | ||
3685 | gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, color, outer_color ); | ||
3686 | } | ||
3687 | i++; | ||
3688 | } | ||
3689 | } | ||
3690 | glPopMatrix(); | ||
3691 | } | ||
3692 | |||
3693 | // virtual | ||
3694 | BOOL LLPieMenu::append(LLMenuItemGL *item) | ||
3695 | { | ||
3696 | item->setBriefItem(TRUE); | ||
3697 | item->setFont( LLFontGL::sSansSerifSmall ); | ||
3698 | return LLMenuGL::append(item); | ||
3699 | } | ||
3700 | |||
3701 | // virtual | ||
3702 | BOOL LLPieMenu::appendSeparator(const LLString &separator_name) | ||
3703 | { | ||
3704 | LLMenuItemGL* separator = new LLMenuItemBlankGL(); | ||
3705 | separator->setFont( LLFontGL::sSansSerifSmall ); | ||
3706 | return append( separator ); | ||
3707 | } | ||
3708 | |||
3709 | |||
3710 | // virtual | ||
3711 | BOOL LLPieMenu::appendMenu(LLPieMenu *menu, | ||
3712 | enabled_callback enabled_cb, | ||
3713 | void* user_data) | ||
3714 | { | ||
3715 | if (menu == this) | ||
3716 | { | ||
3717 | llerrs << "Can't attach a pie menu to itself" << llendl; | ||
3718 | } | ||
3719 | LLPieMenuBranch *item; | ||
3720 | item = new LLPieMenuBranch(menu->getName(), menu->getLabel(), menu, enabled_cb, user_data); | ||
3721 | getParent()->addChild(item->getBranch()); | ||
3722 | item->setFont( LLFontGL::sSansSerifSmall ); | ||
3723 | return append( item ); | ||
3724 | } | ||
3725 | |||
3726 | // virtual | ||
3727 | void LLPieMenu::arrange() | ||
3728 | { | ||
3729 | const S32 rect_height = 180; | ||
3730 | const S32 rect_width = 180; | ||
3731 | |||
3732 | // all divide by 6 | ||
3733 | const S32 CARD_X = 60; | ||
3734 | const S32 DIAG_X = 48; | ||
3735 | const S32 CARD_Y = 76; | ||
3736 | const S32 DIAG_Y = 42; | ||
3737 | |||
3738 | const S32 ITEM_CENTER_X[] = { CARD_X, DIAG_X, 0, -DIAG_X, -CARD_X, -DIAG_X, 0, DIAG_X }; | ||
3739 | const S32 ITEM_CENTER_Y[] = { 0, DIAG_Y, CARD_Y, DIAG_Y, 0, -DIAG_Y, -CARD_Y, -DIAG_Y }; | ||
3740 | |||
3741 | LLRect rect; | ||
3742 | |||
3743 | S32 font_height = 0; | ||
3744 | if( mItems.size() ) | ||
3745 | { | ||
3746 | font_height = (*mItems.begin())->getNominalHeight(); | ||
3747 | } | ||
3748 | S32 item_width = 0; | ||
3749 | |||
3750 | // F32 sin_delta = OO_SQRT2; // sin(45 deg) | ||
3751 | // F32 cos_delta = OO_SQRT2; // cos(45 deg) | ||
3752 | |||
3753 | // TODO: Compute actual bounding rect for menu | ||
3754 | |||
3755 | mRect.setOriginAndSize(mRect.mLeft, mRect.mBottom, rect_width, rect_height ); | ||
3756 | |||
3757 | // place items around a circle, with item 0 at positive X, | ||
3758 | // rotating counter-clockwise | ||
3759 | item_list_t::iterator item_iter; | ||
3760 | S32 i = 0; | ||
3761 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
3762 | { | ||
3763 | LLMenuItemGL *item = *item_iter; | ||
3764 | |||
3765 | item_width = item->getNominalWidth(); | ||
3766 | |||
3767 | // Put in the right place around a circle centered at 0,0 | ||
3768 | rect.setCenterAndSize(ITEM_CENTER_X[i], | ||
3769 | ITEM_CENTER_Y[i], | ||
3770 | item_width, font_height ); | ||
3771 | |||
3772 | // Correct for the actual rectangle size | ||
3773 | rect.translate( rect_width/2, rect_height/2 ); | ||
3774 | |||
3775 | item->setRect( rect ); | ||
3776 | |||
3777 | // Make sure enablement is correct | ||
3778 | item->buildDrawLabel(); | ||
3779 | i++; | ||
3780 | } | ||
3781 | } | ||
3782 | |||
3783 | LLMenuItemGL *LLPieMenu::pieItemFromXY(S32 x, S32 y) | ||
3784 | { | ||
3785 | // We might have shifted this menu on draw. If so, we need | ||
3786 | // to shift over mouseup events until we get a hover event. | ||
3787 | //x += mShiftHoriz; | ||
3788 | //y += mShiftVert; | ||
3789 | |||
3790 | // An arc of the pie menu is 45 degrees | ||
3791 | const F32 ARC_DEG = 45.f; | ||
3792 | S32 delta_x = x - mRect.getWidth() / 2; | ||
3793 | S32 delta_y = y - mRect.getHeight() / 2; | ||
3794 | |||
3795 | // circle safe zone in the center | ||
3796 | S32 dist_squared = delta_x*delta_x + delta_y*delta_y; | ||
3797 | if (dist_squared < PIE_CENTER_SIZE*PIE_CENTER_SIZE) | ||
3798 | { | ||
3799 | return NULL; | ||
3800 | } | ||
3801 | |||
3802 | // infinite radius is only used with right clicks | ||
3803 | S32 radius = llmax( mRect.getWidth()/2, mRect.getHeight()/2 ); | ||
3804 | if (!(mUseInfiniteRadius && mRightMouseDown) && dist_squared > radius * radius) | ||
3805 | { | ||
3806 | return NULL; | ||
3807 | } | ||
3808 | |||
3809 | F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x); | ||
3810 | |||
3811 | // rotate marks CCW so that east = [0, ARC_DEG) instead of | ||
3812 | // [-ARC_DEG/2, ARC_DEG/2) | ||
3813 | angle += ARC_DEG / 2.f; | ||
3814 | |||
3815 | // make sure we're only using positive angles | ||
3816 | if (angle < 0.f) angle += 360.f; | ||
3817 | |||
3818 | S32 which = S32( angle / ARC_DEG ); | ||
3819 | |||
3820 | if (0 <= which && which < (S32)mItems.size() ) | ||
3821 | { | ||
3822 | item_list_t::iterator item_iter; | ||
3823 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
3824 | { | ||
3825 | if (which == 0) | ||
3826 | { | ||
3827 | return (*item_iter); | ||
3828 | } | ||
3829 | which--; | ||
3830 | } | ||
3831 | } | ||
3832 | |||
3833 | return NULL; | ||
3834 | } | ||
3835 | |||
3836 | S32 LLPieMenu::pieItemIndexFromXY(S32 x, S32 y) | ||
3837 | { | ||
3838 | // An arc of the pie menu is 45 degrees | ||
3839 | const F32 ARC_DEG = 45.f; | ||
3840 | // correct for non-square pixels | ||
3841 | S32 delta_x = x - mRect.getWidth() / 2; | ||
3842 | S32 delta_y = y - mRect.getHeight() / 2; | ||
3843 | |||
3844 | // circle safe zone in the center | ||
3845 | if (delta_x*delta_x + delta_y*delta_y < PIE_CENTER_SIZE*PIE_CENTER_SIZE) | ||
3846 | { | ||
3847 | return -1; | ||
3848 | } | ||
3849 | |||
3850 | F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x); | ||
3851 | |||
3852 | // rotate marks CCW so that east = [0, ARC_DEG) instead of | ||
3853 | // [-ARC_DEG/2, ARC_DEG/2) | ||
3854 | angle += ARC_DEG / 2.f; | ||
3855 | |||
3856 | // make sure we're only using positive angles | ||
3857 | if (angle < 0.f) angle += 360.f; | ||
3858 | |||
3859 | S32 which = S32( angle / ARC_DEG ); | ||
3860 | return which; | ||
3861 | } | ||
3862 | |||
3863 | void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down) | ||
3864 | { | ||
3865 | S32 width = mRect.getWidth(); | ||
3866 | S32 height = mRect.getHeight(); | ||
3867 | |||
3868 | LLView* parent_view = getParent(); | ||
3869 | S32 menu_region_width = parent_view->getRect().getWidth(); | ||
3870 | S32 menu_region_height = parent_view->getRect().getHeight(); | ||
3871 | |||
3872 | BOOL moved = FALSE; | ||
3873 | |||
3874 | S32 local_x, local_y; | ||
3875 | parent_view->screenPointToLocal(x, y, &local_x, &local_y); | ||
3876 | |||
3877 | mRect.setCenterAndSize(local_x, local_y, width, height); | ||
3878 | arrange(); | ||
3879 | |||
3880 | // Adjust the pie rectangle to keep it on screen | ||
3881 | if (mRect.mLeft < 0) | ||
3882 | { | ||
3883 | //mShiftHoriz = 0 - mRect.mLeft; | ||
3884 | //mRect.translate( mShiftHoriz, 0 ); | ||
3885 | mRect.translate( 0 - mRect.mLeft, 0 ); | ||
3886 | moved = TRUE; | ||
3887 | } | ||
3888 | |||
3889 | if (mRect.mRight > menu_region_width) | ||
3890 | { | ||
3891 | //mShiftHoriz = menu_region_width - mRect.mRight; | ||
3892 | //mRect.translate( mShiftHoriz, 0); | ||
3893 | mRect.translate( menu_region_width - mRect.mRight, 0 ); | ||
3894 | moved = TRUE; | ||
3895 | } | ||
3896 | |||
3897 | if (mRect.mBottom < 0) | ||
3898 | { | ||
3899 | //mShiftVert = -mRect.mBottom; | ||
3900 | //mRect.translate( 0, mShiftVert ); | ||
3901 | mRect.translate( 0, 0 - mRect.mBottom ); | ||
3902 | moved = TRUE; | ||
3903 | } | ||
3904 | |||
3905 | |||
3906 | if (mRect.mTop > menu_region_height) | ||
3907 | { | ||
3908 | //mShiftVert = menu_region_height - mRect.mTop; | ||
3909 | //mRect.translate( 0, mShiftVert ); | ||
3910 | mRect.translate( 0, menu_region_height - mRect.mTop ); | ||
3911 | moved = TRUE; | ||
3912 | } | ||
3913 | |||
3914 | // If we had to relocate the pie menu, put the cursor in the | ||
3915 | // center of its rectangle | ||
3916 | if (moved) | ||
3917 | { | ||
3918 | LLCoordGL center; | ||
3919 | center.mX = (mRect.mLeft + mRect.mRight) / 2; | ||
3920 | center.mY = (mRect.mTop + mRect.mBottom) / 2; | ||
3921 | |||
3922 | LLUI::setCursorPositionLocal(getParent(), center.mX, center.mY); | ||
3923 | } | ||
3924 | |||
3925 | // *FIX: what happens when mouse buttons reversed? | ||
3926 | mRightMouseDown = mouse_down; | ||
3927 | mFirstMouseDown = mouse_down; | ||
3928 | mUseInfiniteRadius = TRUE; | ||
3929 | if (!mFirstMouseDown) | ||
3930 | { | ||
3931 | make_ui_sound("UISndPieMenuAppear"); | ||
3932 | } | ||
3933 | |||
3934 | LLView::setVisible(TRUE); | ||
3935 | |||
3936 | // we want all mouse events in case user does quick right click again off of pie menu | ||
3937 | // rectangle, to support gestural menu traversal | ||
3938 | gFocusMgr.setMouseCapture(this, NULL); | ||
3939 | |||
3940 | if (mouse_down) | ||
3941 | { | ||
3942 | mShrinkBorderTimer.stop(); | ||
3943 | } | ||
3944 | else | ||
3945 | { | ||
3946 | mShrinkBorderTimer.start(); | ||
3947 | } | ||
3948 | } | ||
3949 | |||
3950 | void LLPieMenu::hide(BOOL item_selected) | ||
3951 | { | ||
3952 | if (!getVisible()) return; | ||
3953 | |||
3954 | if (mHoverItem) | ||
3955 | { | ||
3956 | mHoverItem->setHighlight( FALSE ); | ||
3957 | mHoverItem = NULL; | ||
3958 | } | ||
3959 | |||
3960 | make_ui_sound("UISndPieMenuHide"); | ||
3961 | |||
3962 | mFirstMouseDown = FALSE; | ||
3963 | mRightMouseDown = FALSE; | ||
3964 | mUseInfiniteRadius = FALSE; | ||
3965 | |||
3966 | LLView::setVisible(FALSE); | ||
3967 | |||
3968 | gFocusMgr.setMouseCapture(NULL, NULL); | ||
3969 | } | ||
3970 | |||
3971 | ///============================================================================ | ||
3972 | /// Class LLMenuBarGL | ||
3973 | ///============================================================================ | ||
3974 | |||
3975 | // Default constructor | ||
3976 | LLMenuBarGL::LLMenuBarGL( const LLString& name ) : LLMenuGL ( name, name ) | ||
3977 | { | ||
3978 | mHorizontalLayout = TRUE; | ||
3979 | setCanTearOff(FALSE); | ||
3980 | mKeepFixedSize = TRUE; | ||
3981 | mAltKeyTrigger = FALSE; | ||
3982 | } | ||
3983 | |||
3984 | // Default destructor | ||
3985 | LLMenuBarGL::~LLMenuBarGL() | ||
3986 | { | ||
3987 | std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer()); | ||
3988 | mAccelerators.clear(); | ||
3989 | } | ||
3990 | |||
3991 | // virtual | ||
3992 | LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const | ||
3993 | { | ||
3994 | // Sorty of hacky: reparent items to this and then back at the end of the export | ||
3995 | LLView *orig_parent = NULL; | ||
3996 | item_list_t::const_iterator item_iter; | ||
3997 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
3998 | { | ||
3999 | LLMenuItemGL* child = *item_iter; | ||
4000 | LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child; | ||
4001 | LLMenuGL *menu = branch->getBranch(); | ||
4002 | orig_parent = menu->getParent(); | ||
4003 | menu->updateParent((LLView *)this); | ||
4004 | } | ||
4005 | |||
4006 | LLXMLNodePtr node = LLMenuGL::getXML(); | ||
4007 | |||
4008 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
4009 | { | ||
4010 | LLMenuItemGL* child = *item_iter; | ||
4011 | LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child; | ||
4012 | LLMenuGL *menu = branch->getBranch(); | ||
4013 | menu->updateParent(orig_parent); | ||
4014 | } | ||
4015 | |||
4016 | return node; | ||
4017 | } | ||
4018 | |||
4019 | LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) | ||
4020 | { | ||
4021 | LLString name("menu"); | ||
4022 | node->getAttributeString("name", name); | ||
4023 | |||
4024 | BOOL opaque = FALSE; | ||
4025 | node->getAttributeBOOL("opaque", opaque); | ||
4026 | |||
4027 | LLMenuBarGL *menubar = new LLMenuBarGL(name); | ||
4028 | |||
4029 | LLViewHandle parent_handle = LLViewHandle::sDeadHandle; | ||
4030 | if (parent->getWidgetType() == WIDGET_TYPE_FLOATER) | ||
4031 | { | ||
4032 | parent_handle = ((LLFloater*)parent)->getHandle(); | ||
4033 | } | ||
4034 | |||
4035 | // We need to have the rect early so that it's around when building | ||
4036 | // the menu items | ||
4037 | LLRect view_rect; | ||
4038 | createRect(node, view_rect, parent, menubar->getRequiredRect()); | ||
4039 | menubar->setRect(view_rect); | ||
4040 | |||
4041 | if (node->hasAttribute("drop_shadow")) | ||
4042 | { | ||
4043 | BOOL drop_shadow = FALSE; | ||
4044 | node->getAttributeBOOL("drop_shadow", drop_shadow); | ||
4045 | menubar->setDropShadowed(drop_shadow); | ||
4046 | } | ||
4047 | |||
4048 | menubar->setBackgroundVisible(opaque); | ||
4049 | LLColor4 color(0,0,0,0); | ||
4050 | if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color)) | ||
4051 | { | ||
4052 | menubar->setBackgroundColor(color); | ||
4053 | } | ||
4054 | |||
4055 | LLXMLNodePtr child; | ||
4056 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | ||
4057 | { | ||
4058 | if (child->hasName("menu")) | ||
4059 | { | ||
4060 | LLMenuGL *menu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory); | ||
4061 | // because of lazy initialization, have to disable tear off functionality | ||
4062 | // and then re-enable with proper parent handle | ||
4063 | if (menu->getCanTearOff()) | ||
4064 | { | ||
4065 | menu->setCanTearOff(FALSE); | ||
4066 | menu->setCanTearOff(TRUE, parent_handle); | ||
4067 | } | ||
4068 | menubar->appendMenu(menu); | ||
4069 | if (LLMenuGL::sDefaultMenuContainer != NULL) | ||
4070 | { | ||
4071 | menu->updateParent(LLMenuGL::sDefaultMenuContainer); | ||
4072 | } | ||
4073 | else | ||
4074 | { | ||
4075 | menu->updateParent(parent); | ||
4076 | } | ||
4077 | } | ||
4078 | } | ||
4079 | |||
4080 | menubar->initFromXML(node, parent); | ||
4081 | |||
4082 | BOOL create_jump_keys = FALSE; | ||
4083 | node->getAttributeBOOL("create_jump_keys", create_jump_keys); | ||
4084 | if (create_jump_keys) | ||
4085 | { | ||
4086 | menubar->createJumpKeys(); | ||
4087 | } | ||
4088 | |||
4089 | return menubar; | ||
4090 | } | ||
4091 | |||
4092 | BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) | ||
4093 | { | ||
4094 | if (getHighlightedItem() && mask == MASK_NONE) | ||
4095 | { | ||
4096 | // unmodified key accelerators are ignored when navigating menu | ||
4097 | // (but are used as jump keys so will still work when appropriate menu is up) | ||
4098 | return FALSE; | ||
4099 | } | ||
4100 | BOOL result = LLMenuGL::handleAcceleratorKey(key, mask); | ||
4101 | if (result && mask & MASK_ALT) | ||
4102 | { | ||
4103 | // ALT key used to trigger hotkey, don't use as shortcut to open menu | ||
4104 | mAltKeyTrigger = FALSE; | ||
4105 | } | ||
4106 | |||
4107 | if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) | ||
4108 | { | ||
4109 | if (getHighlightedItem()) | ||
4110 | { | ||
4111 | clearHoverItem(); | ||
4112 | } | ||
4113 | else | ||
4114 | { | ||
4115 | highlightNextItem(NULL); | ||
4116 | LLMenuGL::setKeyboardMode(TRUE); | ||
4117 | } | ||
4118 | return TRUE; | ||
4119 | } | ||
4120 | |||
4121 | return result; | ||
4122 | } | ||
4123 | |||
4124 | BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) | ||
4125 | { | ||
4126 | if(key == KEY_ALT) | ||
4127 | { | ||
4128 | mAltKeyTrigger = TRUE; | ||
4129 | } | ||
4130 | else // if any key other than ALT hit, clear out waiting for Alt key mode | ||
4131 | { | ||
4132 | mAltKeyTrigger = FALSE; | ||
4133 | } | ||
4134 | |||
4135 | // before processing any other key, check to see if ALT key has triggered menu access | ||
4136 | checkMenuTrigger(); | ||
4137 | |||
4138 | return LLMenuGL::handleKeyHere(key, mask, called_from_parent); | ||
4139 | } | ||
4140 | |||
4141 | BOOL LLMenuBarGL::handleJumpKey(KEY key) | ||
4142 | { | ||
4143 | // perform case-insensitive comparison | ||
4144 | key = toupper(key); | ||
4145 | navigation_key_map_t::iterator found_it = mJumpKeys.find(key); | ||
4146 | if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) | ||
4147 | { | ||
4148 | // switch to keyboard navigation mode | ||
4149 | LLMenuGL::setKeyboardMode(TRUE); | ||
4150 | |||
4151 | found_it->second->setHighlight(TRUE); | ||
4152 | found_it->second->doIt(); | ||
4153 | } | ||
4154 | return TRUE; | ||
4155 | } | ||
4156 | |||
4157 | void LLMenuBarGL::draw() | ||
4158 | { | ||
4159 | LLMenuItemGL* itemp = getHighlightedItem(); | ||
4160 | // If we are in mouse-control mode and the mouse cursor is not hovering over | ||
4161 | // the current highlighted menu item and it isn't open, then remove the highlight. | ||
4162 | // This is done via a polling mechanism here, as we don't receive notifications when | ||
4163 | // the mouse cursor moves off of us | ||
4164 | if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode()) | ||
4165 | { | ||
4166 | clearHoverItem(); | ||
4167 | } | ||
4168 | |||
4169 | checkMenuTrigger(); | ||
4170 | |||
4171 | LLMenuGL::draw(); | ||
4172 | } | ||
4173 | |||
4174 | void LLMenuBarGL::checkMenuTrigger() | ||
4175 | { | ||
4176 | // has the ALT key been pressed and subsequently released? | ||
4177 | if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT)) | ||
4178 | { | ||
4179 | // if alt key was released quickly, treat it as a menu access key | ||
4180 | // otherwise it was probably an Alt-zoom or similar action | ||
4181 | if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= LLUI::sConfigGroup->getF32("MenuAccessKeyTime") || | ||
4182 | gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2) | ||
4183 | { | ||
4184 | if (getHighlightedItem()) | ||
4185 | { | ||
4186 | clearHoverItem(); | ||
4187 | } | ||
4188 | else | ||
4189 | { | ||
4190 | highlightNextItem(NULL); | ||
4191 | LLMenuGL::setKeyboardMode(TRUE); | ||
4192 | } | ||
4193 | } | ||
4194 | mAltKeyTrigger = FALSE; | ||
4195 | } | ||
4196 | } | ||
4197 | |||
4198 | BOOL LLMenuBarGL::jumpKeysActive() | ||
4199 | { | ||
4200 | // require item to be highlighted to activate key triggers | ||
4201 | // as menu bars are always visible | ||
4202 | return getHighlightedItem() && LLMenuGL::jumpKeysActive(); | ||
4203 | } | ||
4204 | |||
4205 | // rearrange the child rects so they fit the shape of the menu bar. | ||
4206 | void LLMenuBarGL::arrange( void ) | ||
4207 | { | ||
4208 | U32 pos = 0; | ||
4209 | LLRect rect( 0, mRect.getHeight(), 0, 0 ); | ||
4210 | item_list_t::const_iterator item_iter; | ||
4211 | for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) | ||
4212 | { | ||
4213 | LLMenuItemGL* item = *item_iter; | ||
4214 | rect.mLeft = pos; | ||
4215 | pos += item->getNominalWidth(); | ||
4216 | rect.mRight = pos; | ||
4217 | item->setRect( rect ); | ||
4218 | item->buildDrawLabel(); | ||
4219 | } | ||
4220 | } | ||
4221 | |||
4222 | |||
4223 | S32 LLMenuBarGL::getRightmostMenuEdge() | ||
4224 | { | ||
4225 | // Find the last visible menu | ||
4226 | item_list_t::reverse_iterator item_iter; | ||
4227 | for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter) | ||
4228 | { | ||
4229 | if ((*item_iter)->getVisible()) | ||
4230 | { | ||
4231 | break; | ||
4232 | } | ||
4233 | } | ||
4234 | |||
4235 | if (item_iter == mItems.rend()) | ||
4236 | { | ||
4237 | return 0; | ||
4238 | } | ||
4239 | return (*item_iter)->getRect().mRight; | ||
4240 | } | ||
4241 | |||
4242 | // add a vertical separator to this menu | ||
4243 | BOOL LLMenuBarGL::appendSeparator( const LLString &separator_name ) | ||
4244 | { | ||
4245 | LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); | ||
4246 | return append( separator ); | ||
4247 | } | ||
4248 | |||
4249 | // add a menu - this will create a drop down menu. | ||
4250 | BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) | ||
4251 | { | ||
4252 | if( menu == this ) | ||
4253 | { | ||
4254 | llerrs << "** Attempt to attach menu to itself. This is certainly " | ||
4255 | << "a logic error." << llendl; | ||
4256 | } | ||
4257 | |||
4258 | BOOL success = TRUE; | ||
4259 | |||
4260 | LLMenuItemBranchGL* branch = NULL; | ||
4261 | branch = new LLMenuItemBranchDownGL( menu->getName(), menu->getLabel(), menu ); | ||
4262 | success &= branch->addToAcceleratorList(&mAccelerators); | ||
4263 | success &= append( branch ); | ||
4264 | branch->setJumpKey(branch->getJumpKey()); | ||
4265 | return success; | ||
4266 | } | ||
4267 | |||
4268 | BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) | ||
4269 | { | ||
4270 | BOOL handled = FALSE; | ||
4271 | LLView* active_menu = NULL; | ||
4272 | |||
4273 | BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; | ||
4274 | S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; | ||
4275 | S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; | ||
4276 | mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2); | ||
4277 | mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2); | ||
4278 | mLastMouseX = x; | ||
4279 | mLastMouseY = y; | ||
4280 | |||
4281 | // if nothing currently selected or mouse has moved since last call, pick menu item via mouse | ||
4282 | // otherwise let keyboard control it | ||
4283 | if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0) | ||
4284 | { | ||
4285 | // find current active menu | ||
4286 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
4287 | { | ||
4288 | LLView* viewp = *child_it; | ||
4289 | if (((LLMenuItemGL*)viewp)->isOpen()) | ||
4290 | { | ||
4291 | active_menu = viewp; | ||
4292 | } | ||
4293 | } | ||
4294 | |||
4295 | // check for new active menu | ||
4296 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
4297 | { | ||
4298 | LLView* viewp = *child_it; | ||
4299 | S32 local_x = x - viewp->getRect().mLeft; | ||
4300 | S32 local_y = y - viewp->getRect().mBottom; | ||
4301 | if( viewp->getVisible() && | ||
4302 | viewp->getEnabled() && | ||
4303 | viewp->pointInView(local_x, local_y) && | ||
4304 | viewp->handleHover(local_x, local_y, mask)) | ||
4305 | { | ||
4306 | ((LLMenuItemGL*)viewp)->setHighlight(TRUE); | ||
4307 | handled = TRUE; | ||
4308 | if (active_menu && active_menu != viewp) | ||
4309 | { | ||
4310 | ((LLMenuItemGL*)viewp)->doIt(); | ||
4311 | LLMenuGL::setKeyboardMode(FALSE); | ||
4312 | } | ||
4313 | LLMenuGL::setKeyboardMode(FALSE); | ||
4314 | } | ||
4315 | } | ||
4316 | |||
4317 | if (handled) | ||
4318 | { | ||
4319 | // set hover false on inactive menus | ||
4320 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
4321 | { | ||
4322 | LLView* viewp = *child_it; | ||
4323 | S32 local_x = x - viewp->getRect().mLeft; | ||
4324 | S32 local_y = y - viewp->getRect().mBottom; | ||
4325 | if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) | ||
4326 | { | ||
4327 | ((LLMenuItemGL*)viewp)->setHighlight(FALSE); | ||
4328 | } | ||
4329 | } | ||
4330 | } | ||
4331 | } | ||
4332 | |||
4333 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
4334 | |||
4335 | return TRUE; | ||
4336 | } | ||
4337 | |||
4338 | ///============================================================================ | ||
4339 | /// Class LLMenuHolderGL | ||
4340 | ///============================================================================ | ||
4341 | LLMenuHolderGL::LLMenuHolderGL() | ||
4342 | : LLPanel("Menu Holder") | ||
4343 | { | ||
4344 | setMouseOpaque(FALSE); | ||
4345 | sItemActivationTimer.stop(); | ||
4346 | mCanHide = TRUE; | ||
4347 | } | ||
4348 | |||
4349 | LLMenuHolderGL::LLMenuHolderGL(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows) | ||
4350 | : LLPanel(name, rect, FALSE) | ||
4351 | { | ||
4352 | setMouseOpaque(mouse_opaque); | ||
4353 | sItemActivationTimer.stop(); | ||
4354 | mCanHide = TRUE; | ||
4355 | } | ||
4356 | |||
4357 | LLMenuHolderGL::~LLMenuHolderGL() | ||
4358 | { | ||
4359 | } | ||
4360 | |||
4361 | EWidgetType LLMenuHolderGL::getWidgetType() const | ||
4362 | { | ||
4363 | return WIDGET_TYPE_MENU_HOLDER; | ||
4364 | } | ||
4365 | |||
4366 | LLString LLMenuHolderGL::getWidgetTag() const | ||
4367 | { | ||
4368 | return LL_MENU_HOLDER_GL_TAG; | ||
4369 | } | ||
4370 | |||
4371 | void LLMenuHolderGL::draw() | ||
4372 | { | ||
4373 | LLView::draw(); | ||
4374 | // now draw last selected item as overlay | ||
4375 | LLMenuItemGL* selecteditem = (LLMenuItemGL*)LLView::getViewByHandle(sItemLastSelectedHandle); | ||
4376 | if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) | ||
4377 | { | ||
4378 | // make sure toggle items, for example, show the proper state when fading out | ||
4379 | selecteditem->buildDrawLabel(); | ||
4380 | |||
4381 | LLRect item_rect; | ||
4382 | selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this); | ||
4383 | |||
4384 | F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME; | ||
4385 | F32 alpha = lerp(LLMenuItemGL::sHighlightBackground.mV[VALPHA], 0.f, interpolant); | ||
4386 | LLColor4 bg_color(LLMenuItemGL::sHighlightBackground.mV[VRED], | ||
4387 | LLMenuItemGL::sHighlightBackground.mV[VGREEN], | ||
4388 | LLMenuItemGL::sHighlightBackground.mV[VBLUE], | ||
4389 | alpha); | ||
4390 | |||
4391 | LLUI::pushMatrix(); | ||
4392 | { | ||
4393 | LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f); | ||
4394 | selecteditem->getMenu()->drawBackground(selecteditem, bg_color); | ||
4395 | selecteditem->draw(); | ||
4396 | } | ||
4397 | LLUI::popMatrix(); | ||
4398 | } | ||
4399 | } | ||
4400 | |||
4401 | BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) | ||
4402 | { | ||
4403 | BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; | ||
4404 | if (!handled) | ||
4405 | { | ||
4406 | // clicked off of menu, hide them all | ||
4407 | hideMenus(); | ||
4408 | } | ||
4409 | return handled; | ||
4410 | } | ||
4411 | |||
4412 | BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) | ||
4413 | { | ||
4414 | BOOL handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; | ||
4415 | if (!handled) | ||
4416 | { | ||
4417 | // clicked off of menu, hide them all | ||
4418 | hideMenus(); | ||
4419 | } | ||
4420 | return handled; | ||
4421 | } | ||
4422 | |||
4423 | void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) | ||
4424 | { | ||
4425 | if (width != mRect.getWidth() || height != mRect.getHeight()) | ||
4426 | { | ||
4427 | hideMenus(); | ||
4428 | } | ||
4429 | LLView::reshape(width, height, called_from_parent); | ||
4430 | } | ||
4431 | |||
4432 | BOOL LLMenuHolderGL::hasVisibleMenu() | ||
4433 | { | ||
4434 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
4435 | { | ||
4436 | LLView* viewp = *child_it; | ||
4437 | if (viewp->getVisible()) | ||
4438 | { | ||
4439 | return TRUE; | ||
4440 | } | ||
4441 | } | ||
4442 | return FALSE; | ||
4443 | } | ||
4444 | |||
4445 | BOOL LLMenuHolderGL::hideMenus() | ||
4446 | { | ||
4447 | if (!mCanHide) | ||
4448 | { | ||
4449 | return FALSE; | ||
4450 | } | ||
4451 | BOOL menu_visible = hasVisibleMenu(); | ||
4452 | if (menu_visible) | ||
4453 | { | ||
4454 | // clicked off of menu, hide them all | ||
4455 | for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) | ||
4456 | { | ||
4457 | LLView* viewp = *child_it; | ||
4458 | if (viewp->getVisible()) | ||
4459 | { | ||
4460 | viewp->setVisible(FALSE); | ||
4461 | } | ||
4462 | } | ||
4463 | } | ||
4464 | //if (gFocusMgr.childHasKeyboardFocus(this)) | ||
4465 | //{ | ||
4466 | // gFocusMgr.setKeyboardFocus(NULL, NULL); | ||
4467 | //} | ||
4468 | |||
4469 | return menu_visible; | ||
4470 | } | ||
4471 | |||
4472 | void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) | ||
4473 | { | ||
4474 | sItemLastSelectedHandle = item->mViewHandle; | ||
4475 | sItemActivationTimer.start(); | ||
4476 | } | ||
4477 | |||
4478 | ///============================================================================ | ||
4479 | /// Class LLTearOffMenu | ||
4480 | ///============================================================================ | ||
4481 | LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : | ||
4482 | LLFloater(menup->getName(), LLRect(0, 100, 100, 0), menup->getLabel(), FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, FALSE) | ||
4483 | { | ||
4484 | LLRect rect; | ||
4485 | menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView); | ||
4486 | mTargetHeight = (F32)(rect.getHeight() + LLFLOATER_HEADER_SIZE + 5); | ||
4487 | reshape(rect.getWidth(), rect.getHeight()); | ||
4488 | setRect(rect); | ||
4489 | mOldParent = menup->getParent(); | ||
4490 | mOldParent->removeChild(menup); | ||
4491 | |||
4492 | menup->setFollowsAll(); | ||
4493 | addChild(menup); | ||
4494 | menup->setVisible(TRUE); | ||
4495 | menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1); | ||
4496 | |||
4497 | menup->setTornOff(TRUE); | ||
4498 | menup->setDropShadowed(FALSE); | ||
4499 | |||
4500 | mMenu = menup; | ||
4501 | |||
4502 | // highlight first item (tear off item will be disabled) | ||
4503 | mMenu->highlightNextItem(NULL); | ||
4504 | } | ||
4505 | |||
4506 | LLTearOffMenu::~LLTearOffMenu() | ||
4507 | { | ||
4508 | } | ||
4509 | |||
4510 | void LLTearOffMenu::draw() | ||
4511 | { | ||
4512 | mMenu->setBackgroundVisible(mBgOpaque); | ||
4513 | mMenu->arrange(); | ||
4514 | |||
4515 | if (mRect.getHeight() != mTargetHeight) | ||
4516 | { | ||
4517 | // animate towards target height | ||
4518 | reshape(mRect.getWidth(), llceil(lerp((F32)mRect.getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f)))); | ||
4519 | } | ||
4520 | else | ||
4521 | { | ||
4522 | // when in stasis, remain big enough to hold menu contents | ||
4523 | mTargetHeight = (F32)(mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 4); | ||
4524 | reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 5); | ||
4525 | } | ||
4526 | LLFloater::draw(); | ||
4527 | } | ||
4528 | |||
4529 | void LLTearOffMenu::onFocusReceived() | ||
4530 | { | ||
4531 | // if nothing is highlighted, just highlight first item | ||
4532 | if (!mMenu->getHighlightedItem()) | ||
4533 | { | ||
4534 | mMenu->highlightNextItem(NULL); | ||
4535 | } | ||
4536 | |||
4537 | // parent menu items get highlights so navigation logic keeps working | ||
4538 | LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); | ||
4539 | while(parent_menu_item) | ||
4540 | { | ||
4541 | if (parent_menu_item->getMenu()->getVisible()) | ||
4542 | { | ||
4543 | parent_menu_item->setHighlight(TRUE); | ||
4544 | parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); | ||
4545 | } | ||
4546 | else | ||
4547 | { | ||
4548 | break; | ||
4549 | } | ||
4550 | } | ||
4551 | } | ||
4552 | |||
4553 | void LLTearOffMenu::onFocusLost() | ||
4554 | { | ||
4555 | // remove highlight from parent item and our own menu | ||
4556 | mMenu->clearHoverItem(); | ||
4557 | } | ||
4558 | |||
4559 | BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) | ||
4560 | { | ||
4561 | // pass keystrokes down to menu | ||
4562 | return mMenu->handleUnicodeChar(uni_char, TRUE); | ||
4563 | } | ||
4564 | |||
4565 | BOOL LLTearOffMenu::handleKey(KEY key, MASK mask, BOOL called_from_parent) | ||
4566 | { | ||
4567 | if (!mMenu->getHighlightedItem()) | ||
4568 | { | ||
4569 | if (key == KEY_UP) | ||
4570 | { | ||
4571 | mMenu->highlightPrevItem(NULL); | ||
4572 | return TRUE; | ||
4573 | } | ||
4574 | else if (key == KEY_DOWN) | ||
4575 | { | ||
4576 | mMenu->highlightNextItem(NULL); | ||
4577 | return TRUE; | ||
4578 | } | ||
4579 | } | ||
4580 | // pass keystrokes down to menu | ||
4581 | return mMenu->handleKey(key, mask, TRUE); | ||
4582 | } | ||
4583 | |||
4584 | void LLTearOffMenu::translate(S32 x, S32 y) | ||
4585 | { | ||
4586 | if (x != 0 && y != 0) | ||
4587 | { | ||
4588 | // hide open sub-menus by clearing current hover item | ||
4589 | mMenu->clearHoverItem(); | ||
4590 | } | ||
4591 | LLFloater::translate(x, y); | ||
4592 | } | ||
4593 | |||
4594 | //static | ||
4595 | LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) | ||
4596 | { | ||
4597 | LLTearOffMenu* tearoffp = new LLTearOffMenu(menup); | ||
4598 | // keep onscreen | ||
4599 | gFloaterView->adjustToFitScreen(tearoffp, FALSE); | ||
4600 | tearoffp->open(); | ||
4601 | return tearoffp; | ||
4602 | } | ||
4603 | |||
4604 | void LLTearOffMenu::onClose(bool app_quitting) | ||
4605 | { | ||
4606 | removeChild(mMenu); | ||
4607 | mOldParent->addChild(mMenu); | ||
4608 | mMenu->clearHoverItem(); | ||
4609 | mMenu->setFollowsNone(); | ||
4610 | mMenu->setBackgroundVisible(TRUE); | ||
4611 | mMenu->setVisible(FALSE); | ||
4612 | mMenu->setTornOff(FALSE); | ||
4613 | mMenu->setDropShadowed(TRUE); | ||
4614 | destroy(); | ||
4615 | } | ||
4616 | |||
4617 | ///============================================================================ | ||
4618 | /// Class LLEditMenuHandlerMgr | ||
4619 | ///============================================================================ | ||
4620 | LLEditMenuHandlerMgr& LLEditMenuHandlerMgr::getInstance() | ||
4621 | { | ||
4622 | static LLEditMenuHandlerMgr instance; | ||
4623 | return instance; | ||
4624 | } | ||
4625 | |||
4626 | LLEditMenuHandlerMgr::LLEditMenuHandlerMgr() | ||
4627 | { | ||
4628 | } | ||
4629 | |||
4630 | LLEditMenuHandlerMgr::~LLEditMenuHandlerMgr() | ||
4631 | { | ||
4632 | } | ||
4633 | |||
4634 | ///============================================================================ | ||
4635 | /// Local function definitions | ||
4636 | ///============================================================================ | ||