aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/llmenugl.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/llmenugl.cpp
parentREADME.txt (diff)
downloadmeta-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.cpp4636
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
65LLView *LLMenuGL::sDefaultMenuContainer = NULL;
66
67S32 MENU_BAR_HEIGHT = 0;
68S32 MENU_BAR_WIDTH = 0;
69
70///============================================================================
71/// Local function declarations, constants, enums, and typedefs
72///============================================================================
73
74const LLString SEPARATOR_NAME("separator");
75const LLString TEAROFF_SEPARATOR_LABEL( "~~~~~~~~~~~" );
76const LLString SEPARATOR_LABEL( "-----------" );
77const LLString VERTICAL_SEPARATOR_LABEL( "|" );
78
79const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
80
81const U32 LEFT_PAD_PIXELS = 3;
82const U32 LEFT_WIDTH_PIXELS = 15;
83const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
84
85const U32 RIGHT_PAD_PIXELS = 2;
86const U32 RIGHT_WIDTH_PIXELS = 15;
87const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
88
89const U32 ACCEL_PAD_PIXELS = 10;
90const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
91
92const U32 BRIEF_PAD_PIXELS = 2;
93
94const U32 SEPARATOR_HEIGHT_PIXELS = 8;
95const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
96const S32 MENU_ITEM_PADDING = 4;
97
98const LLString BOOLEAN_TRUE_PREFIX( "X" );
99const LLString BRANCH_SUFFIX( ">" );
100const LLString ARROW_UP ("^^^^^^^");
101const LLString ARROW_DOWN("vvvvvvv");
102
103const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
104
105const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
106
107LLColor4 LLMenuItemGL::sEnabledColor( 0.0f, 0.0f, 0.0f, 1.0f );
108LLColor4 LLMenuItemGL::sDisabledColor( 0.5f, 0.5f, 0.5f, 1.0f );
109LLColor4 LLMenuItemGL::sHighlightBackground( 0.0f, 0.0f, 0.7f, 1.0f );
110LLColor4 LLMenuItemGL::sHighlightForeground( 1.0f, 1.0f, 1.0f, 1.0f );
111BOOL LLMenuItemGL::sDropShadowText = TRUE;
112
113LLColor4 LLMenuGL::sDefaultBackgroundColor( 0.25f, 0.25f, 0.25f, 0.75f );
114BOOL LLMenuGL::sKeyboardMode = FALSE;
115
116LLViewHandle LLMenuHolderGL::sItemLastSelectedHandle;
117LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
118//LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
119
120const S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole
121const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
122const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
123
124const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
125
126///============================================================================
127/// Class LLMenuItemGL
128///============================================================================
129
130// Default constructor
131LLMenuItemGL::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
148LLXMLNodePtr 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
179BOOL 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
204BOOL 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
214BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
215{
216 mGotHover = TRUE;
217 getWindow()->setCursor(UI_CURSOR_ARROW);
218 return TRUE;
219}
220
221void 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
228BOOL 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.
273void 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
309void LLMenuItemGL::setJumpKey(KEY key)
310{
311 mJumpKey = LLStringOps::toUpper((char)key);
312}
313
314KEY LLMenuItemGL::getJumpKey()
315{
316 return mJumpKey;
317}
318
319
320// set the font used by all of the menu objects
321void LLMenuItemGL::setFont(LLFontGL* font)
322{
323 mFont = font;
324}
325
326// returns the height in pixels for the current font.
327U32 LLMenuItemGL::getNominalHeight( void )
328{
329 return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING;
330}
331
332// functions to control the color scheme
333void LLMenuItemGL::setEnabledColor( const LLColor4& color )
334{
335 sEnabledColor = color;
336}
337
338void LLMenuItemGL::setDisabledColor( const LLColor4& color )
339{
340 sDisabledColor = color;
341}
342
343void LLMenuItemGL::setHighlightBGColor( const LLColor4& color )
344{
345 sHighlightBackground = color;
346}
347
348void LLMenuItemGL::setHighlightFGColor( const LLColor4& color )
349{
350 sHighlightForeground = color;
351}
352
353
354// change the label
355void LLMenuItemGL::setLabel( const LLString& label )
356{
357 mLabel = label;
358}
359
360// Get the parent menu for this item
361LLMenuGL* 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.
370U32 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
395void LLMenuItemGL::buildDrawLabel( void )
396{
397 mDrawAccelLabel.clear();
398 LLString st = mDrawAccelLabel.getString();
399 appendAcceleratorString( st );
400 mDrawAccelLabel = st;
401}
402
403void 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
429BOOL LLMenuItemGL::isActive( void ) const
430{
431 return FALSE;
432}
433
434// determine if this object represents an open sub-menu
435BOOL LLMenuItemGL::isOpen( void ) const
436{
437 return FALSE;
438}
439
440BOOL 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
474BOOL 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
494void 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
573BOOL 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
585class LLMenuItemSeparatorGL : public LLMenuItemGL
586{
587public:
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
606LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const LLString &name ) :
607 LLMenuItemGL( SEPARATOR_NAME, SEPARATOR_LABEL )
608{
609}
610
611void 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
619BOOL 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
632BOOL 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
645BOOL 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
667class LLMenuItemVerticalSeparatorGL
668: public LLMenuItemSeparatorGL
669{
670public:
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
679LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
680{
681 setLabel( VERTICAL_SEPARATOR_LABEL );
682}
683
684//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
685// Class LLMenuItemTearOffGL
686//
687// This class represents a separator.
688//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
689LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLViewHandle parent_floater_handle) :
690 LLMenuItemGL("tear off", TEAROFF_SEPARATOR_LABEL),
691 mParentHandle(parent_floater_handle)
692{
693}
694
695EWidgetType LLMenuItemTearOffGL::getWidgetType() const
696{
697 return WIDGET_TYPE_TEAROFF_MENU;
698}
699
700LLString LLMenuItemTearOffGL::getWidgetTag() const
701{
702 return LL_MENU_ITEM_TEAR_OFF_GL_TAG;
703}
704
705void 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
736void 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
759U32 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
767class LLMenuItemBlankGL : public LLMenuItemGL
768{
769public:
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
781LLMenuItemBlankGL::LLMenuItemBlankGL( void )
782: LLMenuItemGL( "", "" )
783{
784 mEnabled = FALSE;
785}
786
787///============================================================================
788/// Class LLMenuItemCallGL
789///============================================================================
790
791LLMenuItemCallGL::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
809LLMenuItemCallGL::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
826LLMenuItemCallGL::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
845LLMenuItemCallGL::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
863void 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
883void 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
904bool 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
922LLXMLNodePtr 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
946void 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
960EWidgetType LLMenuItemCallGL::getWidgetType() const
961{
962 return WIDGET_TYPE_MENU_ITEM_CALL;
963}
964
965LLString LLMenuItemCallGL::getWidgetTag() const
966{
967 return LL_MENU_ITEM_CALL_GL_TAG;
968}
969
970void 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
987BOOL 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
1012LLMenuItemCheckGL::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
1025LLMenuItemCheckGL::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
1037LLMenuItemCheckGL::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
1051void 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
1072bool 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
1092LLXMLNodePtr LLMenuItemCheckGL::getXML(bool save_children) const
1093{
1094 LLXMLNodePtr node = LLMenuItemCallGL::getXML();
1095 return node;
1096}
1097
1098EWidgetType LLMenuItemCheckGL::getWidgetType() const
1099{
1100 return WIDGET_TYPE_MENU_ITEM_CHECK;
1101}
1102
1103LLString LLMenuItemCheckGL::getWidgetTag() const
1104{
1105 return LL_MENU_ITEM_CHECK_GL_TAG;
1106}
1107
1108// called to rebuild the draw label
1109void 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
1127LLMenuItemToggleGL::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
1134LLMenuItemToggleGL::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
1143void 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.
1160void 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
1177class LLMenuItemBranchGL : public LLMenuItemGL
1178{
1179protected:
1180 LLMenuGL* mBranch;
1181
1182public:
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
1234LLMenuItemBranchGL::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
1244LLView* 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
1254EWidgetType LLMenuItemBranchGL::getWidgetType() const
1255{
1256 return WIDGET_TYPE_MENU_ITEM_BRANCH;
1257}
1258
1259LLString LLMenuItemBranchGL::getWidgetTag() const
1260{
1261 return LL_MENU_ITEM_BRANCH_GL_TAG;
1262}
1263
1264// virtual
1265BOOL 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
1278BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
1279{
1280 return mBranch->handleAcceleratorKey(key, mask);
1281}
1282
1283// virtual
1284LLXMLNodePtr 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
1297BOOL 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
1314void 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.
1324void 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
1336BOOL 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
1352BOOL 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)
1370void 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
1414void LLMenuItemBranchGL::setEnabledSubMenus(BOOL enabled)
1415{
1416 mBranch->setEnabledSubMenus(enabled);
1417}
1418
1419void 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"
1430BOOL LLMenuItemBranchGL::isActive( void ) const
1431{
1432 return isOpen() && mBranch->getHighlightedItem();
1433}
1434
1435BOOL LLMenuItemBranchGL::isOpen( void ) const
1436{
1437 return mBranch->isOpen();
1438}
1439
1440void 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
1449void LLMenuItemBranchGL::onVisibilityChange( BOOL curVisibilityIn )
1450{
1451 if (curVisibilityIn == FALSE && mBranch->getVisible() && !mBranch->getTornOff())
1452 {
1453 mBranch->setVisible(FALSE);
1454 }
1455}
1456
1457BOOL 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
1494void 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
1550class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
1551{
1552protected:
1553
1554public:
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
1589LLMenuItemBranchDownGL::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.
1600U32 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
1608void LLMenuItemBranchDownGL::buildDrawLabel( void )
1609{
1610 mDrawAccelLabel.clear();
1611 LLString st = mDrawAccelLabel;
1612 appendAcceleratorString( st );
1613 mDrawAccelLabel = st;
1614}
1615
1616void 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)
1666void 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
1689BOOL 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
1697BOOL 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
1707BOOL 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
1720BOOL 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
1782void 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
1844LLMenuGL::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
1869LLMenuGL::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
1895LLMenuGL::~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
1903void 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
1923LLXMLNodePtr 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
1956void 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)
2216BOOL 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
2237BOOL 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
2259LLView* 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
2329void LLMenuGL::setDefaultBackgroundColor( const LLColor4& color )
2330{
2331 sDefaultBackgroundColor = color;
2332}
2333
2334void LLMenuGL::setBackgroundColor( const LLColor4& color )
2335{
2336 mBackgroundColor = color;
2337}
2338
2339// rearrange the child rects so they fit the shape of the menu.
2340void 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
2465void 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
2484void 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
2512void 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
2609void LLMenuGL::empty( void )
2610{
2611 mItems.clear();
2612
2613 deleteAllChildren();
2614
2615}
2616
2617// Adjust rectangle of the menu
2618void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
2619{
2620 mRect.mLeft = left;
2621 mRect.mBottom = bottom;
2622 arrange();
2623}
2624
2625BOOL 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.
2650BOOL 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
2659BOOL 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
2666BOOL 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
2686void 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.
2698void 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
2712void 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
2725void 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
2737void LLMenuGL::setDropShadowed( const BOOL shadowed )
2738{
2739 mDropShadowed = shadowed;
2740}
2741
2742void LLMenuGL::setTornOff(BOOL torn_off)
2743{
2744 mTornOff = torn_off;
2745}
2746
2747U32 LLMenuGL::getItemCount()
2748{
2749 return mItems.size();
2750}
2751
2752LLMenuItemGL* 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
2769LLMenuItemGL* 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
2782LLMenuItemGL* 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
2860LLMenuItemGL* 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
2922void 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
2931void 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
2946BOOL 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
2976BOOL 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
2992BOOL 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
3001BOOL 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
3068void 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
3086void 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
3093void 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
3117LLMenuGL* 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
3136BOOL 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
3150void hide_top_view( LLView* view )
3151{
3152 if( view ) view->setVisible( FALSE );
3153}
3154
3155
3156// static
3157void 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//-----------------------------------------------------------------------------
3200class LLPieMenuBranch : public LLMenuItemGL
3201{
3202public:
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
3217protected:
3218 LLPieMenu* mBranch;
3219 enabled_callback mEnabledCallback;
3220 void* mUserData;
3221};
3222
3223LLPieMenuBranch::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
3238void 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.
3276void 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, &center_x, &center_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//-----------------------------------------------------------------------------
3293LLPieMenu::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
3307LLPieMenu::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
3322LLPieMenu::~LLPieMenu()
3323{ }
3324
3325
3326EWidgetType LLPieMenu::getWidgetType() const
3327{
3328 return WIDGET_TYPE_PIE_MENU;
3329}
3330
3331LLString LLPieMenu::getWidgetTag() const
3332{
3333 return LL_PIE_MENU_TAG;
3334}
3335
3336void 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
3361void LLPieMenu::setVisible(BOOL visible)
3362{
3363 if (!visible)
3364 {
3365 hide(FALSE);
3366 }
3367}
3368
3369BOOL 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
3463BOOL 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
3480BOOL 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
3515BOOL 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
3531BOOL 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
3577void 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
3659void 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
3694BOOL LLPieMenu::append(LLMenuItemGL *item)
3695{
3696 item->setBriefItem(TRUE);
3697 item->setFont( LLFontGL::sSansSerifSmall );
3698 return LLMenuGL::append(item);
3699}
3700
3701// virtual
3702BOOL 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
3711BOOL 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
3727void 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
3783LLMenuItemGL *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
3836S32 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
3863void 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
3950void 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
3976LLMenuBarGL::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
3985LLMenuBarGL::~LLMenuBarGL()
3986{
3987 std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
3988 mAccelerators.clear();
3989}
3990
3991// virtual
3992LLXMLNodePtr 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
4019LLView* 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
4092BOOL 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
4124BOOL 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
4141BOOL 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
4157void 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
4174void 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
4198BOOL 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.
4206void 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
4223S32 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
4243BOOL 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.
4250BOOL 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
4268BOOL 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///============================================================================
4341LLMenuHolderGL::LLMenuHolderGL()
4342: LLPanel("Menu Holder")
4343{
4344 setMouseOpaque(FALSE);
4345 sItemActivationTimer.stop();
4346 mCanHide = TRUE;
4347}
4348
4349LLMenuHolderGL::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
4357LLMenuHolderGL::~LLMenuHolderGL()
4358{
4359}
4360
4361EWidgetType LLMenuHolderGL::getWidgetType() const
4362{
4363 return WIDGET_TYPE_MENU_HOLDER;
4364}
4365
4366LLString LLMenuHolderGL::getWidgetTag() const
4367{
4368 return LL_MENU_HOLDER_GL_TAG;
4369}
4370
4371void 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
4401BOOL 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
4412BOOL 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
4423void 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
4432BOOL 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
4445BOOL 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
4472void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
4473{
4474 sItemLastSelectedHandle = item->mViewHandle;
4475 sItemActivationTimer.start();
4476}
4477
4478///============================================================================
4479/// Class LLTearOffMenu
4480///============================================================================
4481LLTearOffMenu::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
4506LLTearOffMenu::~LLTearOffMenu()
4507{
4508}
4509
4510void 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
4529void 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
4553void LLTearOffMenu::onFocusLost()
4554{
4555 // remove highlight from parent item and our own menu
4556 mMenu->clearHoverItem();
4557}
4558
4559BOOL 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
4565BOOL 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
4584void 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
4595LLTearOffMenu* 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
4604void 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///============================================================================
4620LLEditMenuHandlerMgr& LLEditMenuHandlerMgr::getInstance()
4621{
4622 static LLEditMenuHandlerMgr instance;
4623 return instance;
4624}
4625
4626LLEditMenuHandlerMgr::LLEditMenuHandlerMgr()
4627{
4628}
4629
4630LLEditMenuHandlerMgr::~LLEditMenuHandlerMgr()
4631{
4632}
4633
4634///============================================================================
4635/// Local function definitions
4636///============================================================================