llmenugl.cpp

Go to the documentation of this file.
00001 
00032 //*****************************************************************************
00033 //
00034 // This file contains the opengl based menu implementation.
00035 //
00036 // NOTES: A menu label is split into 4 columns. The left column, the
00037 // label colum, the accelerator column, and the right column. The left
00038 // column is used for displaying boolean values for toggle and check
00039 // controls. The right column is used for submenus.
00040 //
00041 //*****************************************************************************
00042 
00043 //#include "llviewerprecompiledheaders.h"
00044 #include "linden_common.h"
00045 
00046 #include "llmenugl.h"
00047 
00048 #include "llmath.h"
00049 #include "llgl.h"
00050 #include "llglimmediate.h"
00051 #include "llfocusmgr.h"
00052 #include "llfont.h"
00053 #include "llcoord.h"
00054 #include "llwindow.h"
00055 #include "llcriticaldamp.h"
00056 #include "lluictrlfactory.h"
00057 
00058 #include "llfontgl.h"
00059 #include "llresmgr.h"
00060 #include "llui.h"
00061 
00062 #include "llglheaders.h"
00063 #include "llstl.h"
00064 
00065 #include "v2math.h"
00066 #include <set>
00067 #include <boost/tokenizer.hpp>
00068 
00069 // static
00070 LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
00071 
00072 S32 MENU_BAR_HEIGHT = 0;
00073 S32 MENU_BAR_WIDTH = 0;
00074 
00078 
00079 const LLString SEPARATOR_NAME("separator");
00080 const LLString TEAROFF_SEPARATOR_LABEL( "~~~~~~~~~~~" );
00081 const LLString SEPARATOR_LABEL( "-----------" );
00082 const LLString VERTICAL_SEPARATOR_LABEL( "|" );
00083 
00084 const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
00085 
00086 const U32 LEFT_PAD_PIXELS = 3;
00087 const U32 LEFT_WIDTH_PIXELS = 15;
00088 const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
00089 
00090 const U32 RIGHT_PAD_PIXELS = 2;
00091 const U32 RIGHT_WIDTH_PIXELS = 15;
00092 const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
00093 
00094 const U32 ACCEL_PAD_PIXELS = 10;
00095 const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
00096 
00097 const U32 BRIEF_PAD_PIXELS = 2;
00098 
00099 const U32 SEPARATOR_HEIGHT_PIXELS = 8;
00100 const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
00101 const S32 MENU_ITEM_PADDING = 4;
00102 
00103 const LLString BOOLEAN_TRUE_PREFIX( "X" );
00104 const LLString BRANCH_SUFFIX( ">" );
00105 const LLString ARROW_UP  ("^^^^^^^");
00106 const LLString ARROW_DOWN("vvvvvvv");
00107 
00108 const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
00109 
00110 const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
00111 
00112 LLColor4 LLMenuItemGL::sEnabledColor( 0.0f, 0.0f, 0.0f, 1.0f );
00113 LLColor4 LLMenuItemGL::sDisabledColor( 0.5f, 0.5f, 0.5f, 1.0f );
00114 LLColor4 LLMenuItemGL::sHighlightBackground( 0.0f, 0.0f, 0.7f, 1.0f );
00115 LLColor4 LLMenuItemGL::sHighlightForeground( 1.0f, 1.0f, 1.0f, 1.0f );
00116 
00117 LLColor4 LLMenuGL::sDefaultBackgroundColor( 0.25f, 0.25f, 0.25f, 0.75f );
00118 BOOL LLMenuGL::sKeyboardMode = FALSE;
00119 
00120 LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
00121 LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
00122 //LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
00123 
00124 const S32 PIE_CENTER_SIZE = 20;         // pixels, radius of center hole
00125 const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
00126 const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
00127 
00128 const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
00129 
00133 
00134 // Default constructor
00135 LLMenuItemGL::LLMenuItemGL( const LLString& name, const LLString& label, KEY key, MASK mask ) :
00136         LLView( name, TRUE ),
00137         mJumpKey(KEY_NONE),
00138         mAcceleratorKey( key ),
00139         mAcceleratorMask( mask ),
00140         mAllowKeyRepeat(FALSE),
00141         mHighlight( FALSE ),
00142         mGotHover( FALSE ),
00143         mBriefItem( FALSE ),
00144         mFont( LLFontGL::sSansSerif ),
00145         mStyle(LLFontGL::NORMAL),
00146         mDrawTextDisabled( FALSE )
00147 {
00148         setLabel( label );
00149 }
00150 
00151 // virtual
00152 LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const
00153 {
00154         LLXMLNodePtr node = LLView::getXML();
00155 
00156         node->createChild("type", TRUE)->setStringValue(getType());
00157 
00158         node->createChild("label", TRUE)->setStringValue(mLabel);
00159 
00160         if (mAcceleratorKey != KEY_NONE)
00161         {
00162                 std::stringstream out;
00163                 if (mAcceleratorMask & MASK_CONTROL)
00164                 {
00165                         out << "control|";
00166                 }
00167                 if (mAcceleratorMask & MASK_ALT)
00168                 {
00169                         out << "alt|";
00170                 }
00171                 if (mAcceleratorMask & MASK_SHIFT)
00172                 {
00173                         out << "shift|";
00174                 }
00175                 out << LLKeyboard::stringFromKey(mAcceleratorKey);
00176 
00177                 node->createChild("shortcut", TRUE)->setStringValue(out.str());
00178                 
00179 #ifdef LL_DARWIN
00180                 // Write in special tag if this key is really a ctrl combination on the Mac
00181                 if (mAcceleratorMask & MASK_MAC_CONTROL)
00182                 {
00183                         node->createChild("useMacCtrl", TRUE)->setBoolValue( TRUE );
00184                 }
00185 #endif // LL_DARWIN
00186         }
00187 
00188         return node;
00189 }
00190 
00191 BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
00192 {
00193         if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
00194         {
00195                 doIt();
00196                 return TRUE;
00197         }
00198         return FALSE;
00199 }
00200 
00201 BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
00202 {
00203         setHover(TRUE);
00204         getWindow()->setCursor(UI_CURSOR_ARROW);
00205         return TRUE;
00206 }
00207 
00208 // This function checks to see if the accelerator key is already in use;
00209 // if not, it will be added to the list
00210 BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
00211 {
00212         LLKeyBinding *accelerator = NULL;
00213 
00214         if (mAcceleratorKey != KEY_NONE)
00215         {
00216                 std::list<LLKeyBinding*>::iterator list_it;
00217                 for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
00218                 {
00219                         accelerator = *list_it;
00220                         if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
00221                         {
00222 
00223                         // *NOTE: get calling code to throw up warning or route
00224                         // warning messages back to app-provided output
00225                         //      LLString warning;
00226                         //      warning.append("Duplicate key binding <");
00227                         //      appendAcceleratorString( warning );
00228                         //      warning.append("> for menu items:\n    ");
00229                         //      warning.append(accelerator->mName);
00230                         //      warning.append("\n    ");
00231                         //      warning.append(mLabel);
00232 
00233                         //      llwarns << warning << llendl;
00234                         //      LLAlertDialog::modalAlert(warning);
00235                                 return FALSE;
00236                         }
00237                 }
00238                 if (!accelerator)
00239                 {                               
00240                         accelerator = new LLKeyBinding;
00241                         if (accelerator)
00242                         {
00243                                 accelerator->mKey = mAcceleratorKey;
00244                                 accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
00245 //                              accelerator->mName = mLabel;
00246                         }
00247                         listp->push_back(accelerator);//addData(accelerator);
00248                 }
00249         }
00250         return TRUE;
00251 }
00252 
00253 // This function appends the character string representation of
00254 // the current accelerator key and mask to the provided string.
00255 void LLMenuItemGL::appendAcceleratorString( LLString& st ) const
00256 {
00257         // break early if this is a silly thing to do.
00258         if( KEY_NONE == mAcceleratorKey )
00259         {
00260                 return;
00261         }
00262 
00263         // Append any masks
00264 #ifdef LL_DARWIN
00265         // Standard Mac names for modifier keys in menu equivalents
00266         // We could use the symbol characters, but they only exist in certain fonts.
00267         if( mAcceleratorMask & MASK_CONTROL )
00268         {
00269                 if ( mAcceleratorMask & MASK_MAC_CONTROL )
00270                 {
00271                         st.append( "Ctrl-" );
00272                 }
00273                 else
00274                 {
00275                         st.append( "Cmd-" );            // Symbol would be "\xE2\x8C\x98"
00276                 }
00277         }
00278         if( mAcceleratorMask & MASK_ALT )
00279                 st.append( "Opt-" );            // Symbol would be "\xE2\x8C\xA5"
00280         if( mAcceleratorMask & MASK_SHIFT )
00281                 st.append( "Shift-" );          // Symbol would be "\xE2\x8C\xA7"
00282 #else
00283         if( mAcceleratorMask & MASK_CONTROL )
00284                 st.append( "Ctrl-" );
00285         if( mAcceleratorMask & MASK_ALT )
00286                 st.append( "Alt-" );
00287         if( mAcceleratorMask & MASK_SHIFT )
00288                 st.append( "Shift-" );
00289 #endif
00290 
00291         LLString keystr = LLKeyboard::stringFromKey( mAcceleratorKey );
00292         if ((mAcceleratorMask & MASK_NORMALKEYS) &&
00293                 (keystr[0] == '-' || keystr[0] == '='))
00294         {
00295                 st.append( " " );
00296         }
00297         st.append( keystr );
00298 }
00299 
00300 void LLMenuItemGL::setJumpKey(KEY key)
00301 {
00302         mJumpKey = LLStringOps::toUpper((char)key);
00303 }
00304 
00305 
00306 // virtual 
00307 U32 LLMenuItemGL::getNominalHeight( void ) const 
00308 { 
00309         return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; 
00310 }
00311 
00312 
00313 // Get the parent menu for this item
00314 LLMenuGL* LLMenuItemGL::getMenu()
00315 {
00316         return (LLMenuGL*) getParent();
00317 }
00318 
00319 
00320 // getNominalWidth() - returns the normal width of this control in
00321 // pixels - this is used for calculating the widest item, as well as
00322 // for horizontal arrangement.
00323 U32 LLMenuItemGL::getNominalWidth( void ) const
00324 {
00325         U32 width;
00326         
00327         if (mBriefItem)
00328         {
00329                 width = BRIEF_PAD_PIXELS;
00330         }
00331         else
00332         {
00333                 width = PLAIN_PAD_PIXELS;
00334         }
00335 
00336         if( KEY_NONE != mAcceleratorKey )
00337         {
00338                 width += ACCEL_PAD_PIXELS;
00339                 LLString temp;
00340                 appendAcceleratorString( temp );
00341                 width += mFont->getWidth( temp );
00342         }
00343         width += mFont->getWidth( mLabel.getWString().c_str() );
00344         return width;
00345 }
00346 
00347 // called to rebuild the draw label
00348 void LLMenuItemGL::buildDrawLabel( void )
00349 {
00350         mDrawAccelLabel.clear();
00351         LLString st = mDrawAccelLabel.getString();
00352         appendAcceleratorString( st );
00353         mDrawAccelLabel = st;
00354 }
00355 
00356 void LLMenuItemGL::doIt( void )
00357 {
00358         // close all open menus by default
00359         // if parent menu is actually visible (and we are not triggering menu item via accelerator)
00360         if (!getMenu()->getTornOff() 
00361                 && getMenu()->getVisible())
00362         {
00363                 LLMenuGL::sMenuContainer->hideMenus();
00364         }
00365 }
00366 
00367 // set the hover status (called by it's menu)
00368  void LLMenuItemGL::setHighlight( BOOL highlight )
00369 {
00370         if (highlight)
00371         {
00372                 getMenu()->clearHoverItem();
00373         }
00374         mHighlight = highlight;
00375 }
00376 
00377 
00378 BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
00379 {
00380         if (getHighlight() && 
00381                 getMenu()->isOpen())
00382         {
00383                 if (key == KEY_UP)
00384                 {
00385                         // switch to keyboard navigation mode
00386                         LLMenuGL::setKeyboardMode(TRUE);
00387 
00388                         getMenu()->highlightPrevItem(this);
00389                         return TRUE;
00390                 }
00391                 else if (key == KEY_DOWN)
00392                 {
00393                         // switch to keyboard navigation mode
00394                         LLMenuGL::setKeyboardMode(TRUE);
00395 
00396                         getMenu()->highlightNextItem(this);
00397                         return TRUE;
00398                 }
00399                 else if (key == KEY_RETURN && mask == MASK_NONE)
00400                 {
00401                         // switch to keyboard navigation mode
00402                         LLMenuGL::setKeyboardMode(TRUE);
00403 
00404                         doIt();
00405                         return TRUE;
00406                 }
00407         }
00408 
00409         return FALSE;
00410 }
00411 
00412 BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK )
00413 {
00414         // switch to mouse navigation mode
00415         LLMenuGL::setKeyboardMode(FALSE);
00416 
00417         doIt();
00418         make_ui_sound("UISndClickRelease");
00419         return TRUE;
00420 }
00421 
00422 BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK )
00423 {
00424         // switch to mouse navigation mode
00425         LLMenuGL::setKeyboardMode(FALSE);
00426 
00427         setHighlight(TRUE);
00428         return TRUE;
00429 }
00430 
00431 
00432 void LLMenuItemGL::draw( void )
00433 {
00434         // *FIX: This can be optimized by using switches. Want to avoid
00435         // that until the functionality is finalized.
00436 
00437         // HACK: Brief items don't highlight.  Pie menu takes care of it.  JC
00438         // let disabled items be highlighted, just don't draw them as such
00439         if( getEnabled() && getHighlight() && !mBriefItem)
00440         {
00441                 gGL.color4fv( sHighlightBackground.mV );
00442                 gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
00443         }
00444 
00445         LLColor4 color;
00446 
00447         U8 font_style = mStyle;
00448         if (getEnabled() && !mDrawTextDisabled )
00449         {
00450                 font_style |= LLFontGL::DROP_SHADOW_SOFT;
00451         }
00452 
00453         if ( getEnabled() && getHighlight() )
00454         {
00455                 color = sHighlightForeground;
00456         }
00457         else if( getEnabled() && !mDrawTextDisabled )
00458         {
00459                 color = sEnabledColor;
00460         }
00461         else
00462         {
00463                 color = sDisabledColor;
00464         }
00465 
00466         // Draw the text on top.
00467         if (mBriefItem)
00468         {
00469                 mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
00470                                            LLFontGL::LEFT, LLFontGL::BOTTOM, font_style );
00471         }
00472         else
00473         {
00474                 if( !mDrawBoolLabel.empty() )
00475                 {
00476                         mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
00477                                                    LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
00478                 }
00479                 mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
00480                                            LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
00481                 if( !mDrawAccelLabel.empty() )
00482                 {
00483                         mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
00484                                                    LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
00485                 }
00486                 if( !mDrawBranchLabel.empty() )
00487                 {
00488                         mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
00489                                                    LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
00490                 }
00491         }
00492 
00493         // underline "jump" key only when keyboard navigation has been initiated
00494         if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
00495         {
00496                 LLString upper_case_label = mLabel.getString();
00497                 LLString::toUpper(upper_case_label);
00498                 std::string::size_type offset = upper_case_label.find(mJumpKey);
00499                 if (offset != std::string::npos)
00500                 {
00501                         S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
00502                         S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
00503                         gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
00504                 }
00505         }
00506 
00507         // clear got hover every frame
00508         setHover(FALSE);
00509 }
00510 
00511 BOOL LLMenuItemGL::setLabelArg( const LLString& key, const LLStringExplicit& text )
00512 {
00513         mLabel.setArg(key, text);
00514         return TRUE;
00515 }
00516 
00517 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00518 // Class LLMenuItemSeparatorGL
00519 //
00520 // This class represents a separator.
00521 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00522 
00523 class LLMenuItemSeparatorGL : public LLMenuItemGL
00524 {
00525 public:
00526         LLMenuItemSeparatorGL( const LLString &name = SEPARATOR_NAME );
00527 
00528         virtual LLString getType() const        { return "separator"; }
00529 
00530         // doIt() - do the primary funcationality of the menu item.
00531         virtual void doIt( void ) {}
00532 
00533         virtual void draw( void );
00534         virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
00535         virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
00536         virtual BOOL handleHover(S32 x, S32 y, MASK mask);
00537 
00538         virtual U32 getNominalHeight( void ) const { return SEPARATOR_HEIGHT_PIXELS; }
00539 };
00540 
00541 LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const LLString &name ) :
00542         LLMenuItemGL( name, SEPARATOR_LABEL )
00543 {
00544 }
00545 
00546 void LLMenuItemSeparatorGL::draw( void )
00547 {
00548         gGL.color4fv( getDisabledColor().mV );
00549         const S32 y = getRect().getHeight() / 2;
00550         const S32 PAD = 6;
00551         gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
00552 }
00553 
00554 BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
00555 {
00556         LLMenuGL* parent_menu = getMenu();
00557         if (y > getRect().getHeight() / 2)
00558         {
00559                 return parent_menu->handleMouseDown(x + getRect().mLeft, getRect().mTop + 1, mask);
00560         }
00561         else
00562         {
00563                 return parent_menu->handleMouseDown(x + getRect().mLeft, getRect().mBottom - 1, mask);
00564         }
00565 }
00566 
00567 BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) 
00568 {
00569         LLMenuGL* parent_menu = getMenu();
00570         if (y > getRect().getHeight() / 2)
00571         {
00572                 return parent_menu->handleMouseUp(x + getRect().mLeft, getRect().mTop + 1, mask);
00573         }
00574         else
00575         {
00576                 return parent_menu->handleMouseUp(x + getRect().mLeft, getRect().mBottom - 1, mask);
00577         }
00578 }
00579 
00580 BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) 
00581 {
00582         LLMenuGL* parent_menu = getMenu();
00583         if (y > getRect().getHeight() / 2)
00584         {
00585                 parent_menu->highlightPrevItem(this, FALSE);
00586                 return FALSE;
00587         }
00588         else
00589         {
00590                 parent_menu->highlightNextItem(this, FALSE);
00591                 return FALSE;
00592         }
00593 }
00594 
00595 
00596 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00597 // Class LLMenuItemVerticalSeparatorGL
00598 //
00599 // This class represents a vertical separator.
00600 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00601 
00602 class LLMenuItemVerticalSeparatorGL
00603 :       public LLMenuItemSeparatorGL
00604 {
00605 public:
00606         LLMenuItemVerticalSeparatorGL( void );
00607 
00608         virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
00609 };
00610 
00611 LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
00612 {
00613         setLabel( VERTICAL_SEPARATOR_LABEL );
00614 }
00615 
00616 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00617 // Class LLMenuItemTearOffGL
00618 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00619 LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLHandle<LLFloater> parent_floater_handle) : 
00620         LLMenuItemGL("tear off", TEAROFF_SEPARATOR_LABEL), 
00621         mParentHandle(parent_floater_handle)
00622 {
00623 }
00624 
00625 
00626 void LLMenuItemTearOffGL::doIt()
00627 {
00628         if (getMenu()->getTornOff())
00629         {
00630                 LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent());
00631                 torn_off_menu->close();
00632         }
00633         else
00634         {
00635                 // transfer keyboard focus and highlight to first real item in list
00636                 if (getHighlight())
00637                 {
00638                         getMenu()->highlightNextItem(this);
00639                 }
00640 
00641                 getMenu()->arrange();
00642 
00643                 LLFloater* parent_floater = mParentHandle.get();
00644                 LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
00645 
00646                 if (tear_off_menu)
00647                 {
00648                         if (parent_floater)
00649                         {
00650                                 parent_floater->addDependentFloater(tear_off_menu, FALSE);
00651                         }
00652 
00653                         // give focus to torn off menu because it will have
00654                         // been taken away when parent menu closes
00655                         tear_off_menu->setFocus(TRUE);
00656                 }
00657         }
00658         LLMenuItemGL::doIt();
00659 }
00660 
00661 void LLMenuItemTearOffGL::draw()
00662 {
00663         // disabled items can be highlighted, but shouldn't render as such
00664         if( getEnabled() && getHighlight() && !isBriefItem())
00665         {
00666                 gGL.color4fv( getHighlightBGColor().mV );
00667                 gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
00668         }
00669 
00670         if (getEnabled())
00671         {
00672                 gGL.color4fv( getEnabledColor().mV );
00673         }
00674         else
00675         {
00676                 gGL.color4fv( getDisabledColor().mV );
00677         }
00678         const S32 y = getRect().getHeight() / 3;
00679         const S32 PAD = 6;
00680         gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
00681         gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
00682 }
00683 
00684 U32 LLMenuItemTearOffGL::getNominalHeight( void ) const 
00685 { 
00686         return TEAROFF_SEPARATOR_HEIGHT_PIXELS; 
00687 }
00688 
00689 
00690 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00691 // Class LLMenuItemBlankGL
00692 //
00693 // This class represents a blank, non-functioning item.
00694 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00695 
00696 class LLMenuItemBlankGL : public LLMenuItemGL
00697 {
00698 public:
00699         LLMenuItemBlankGL( void ) :     LLMenuItemGL( "", "" )
00700         {
00701                 setEnabled(FALSE);
00702         }
00703         virtual void doIt( void ) {}
00704         virtual void draw( void ) {}
00705 };
00706 
00707 
00711 
00712 LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name, 
00713                                                                         const LLString& label, 
00714                                                                         menu_callback clicked_cb, 
00715                                                                     enabled_callback enabled_cb,
00716                                                                         void* user_data,
00717                                                                         KEY key, MASK mask,
00718                                                                         BOOL enabled,
00719                                                                         on_disabled_callback on_disabled_cb) :
00720         LLMenuItemGL( name, label, key, mask ),
00721         mCallback( clicked_cb ),
00722         mEnabledCallback( enabled_cb ),
00723         mLabelCallback(NULL),
00724         mUserData( user_data ),
00725         mOnDisabledCallback(on_disabled_cb)
00726 {
00727         if(!enabled) setEnabled(FALSE);
00728 }
00729 
00730 LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name, 
00731                                                                         menu_callback clicked_cb, 
00732                                                                     enabled_callback enabled_cb,
00733                                                                         void* user_data,
00734                                                                         KEY key, MASK mask,
00735                                                                         BOOL enabled,
00736                                                                         on_disabled_callback on_disabled_cb) :
00737         LLMenuItemGL( name, name, key, mask ),
00738         mCallback( clicked_cb ),
00739         mEnabledCallback( enabled_cb ),
00740         mLabelCallback(NULL),
00741         mUserData( user_data ),
00742         mOnDisabledCallback(on_disabled_cb)
00743 {
00744         if(!enabled) setEnabled(FALSE);
00745 }
00746 
00747 LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name,
00748                                                                    const LLString& label,
00749                                                                    menu_callback clicked_cb,
00750                                                                    enabled_callback enabled_cb,
00751                                                                    label_callback label_cb,
00752                                                                    void* user_data,
00753                                                                    KEY key, MASK mask,
00754                                                                    BOOL enabled,
00755                                                                    on_disabled_callback on_disabled_cb) :
00756         LLMenuItemGL(name, label, key, mask),
00757         mCallback(clicked_cb),
00758         mEnabledCallback(enabled_cb),
00759         mLabelCallback(label_cb),
00760         mUserData(user_data),
00761         mOnDisabledCallback(on_disabled_cb)
00762 {
00763         if(!enabled) setEnabled(FALSE);
00764 }
00765 
00766 LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name,
00767                                                                    menu_callback clicked_cb,
00768                                                                    enabled_callback enabled_cb,
00769                                                                    label_callback label_cb,
00770                                                                    void* user_data,
00771                                                                    KEY key, MASK mask,
00772                                                                    BOOL enabled,
00773                                                                    on_disabled_callback on_disabled_cb) :
00774         LLMenuItemGL(name, name, key, mask),
00775         mCallback(clicked_cb),
00776         mEnabledCallback(enabled_cb),
00777         mLabelCallback(label_cb),
00778         mUserData(user_data),
00779         mOnDisabledCallback(on_disabled_cb)
00780 {
00781         if(!enabled) setEnabled(FALSE);
00782 }
00783 
00784 void LLMenuItemCallGL::setEnabledControl(LLString enabled_control, LLView *context)
00785 {
00786         // Register new listener
00787         if (!enabled_control.empty())
00788         {
00789                 LLControlVariable *control = context->findControl(enabled_control);
00790                 if (!control)
00791                 {
00792                         context->addBoolControl(enabled_control, getEnabled());
00793                         control = context->findControl(enabled_control);
00794                         llassert_always(control);
00795                 }
00796                 control->getSignal()->connect(boost::bind(&LLView::controlListener, _1, getHandle(), std::string("enabled")));
00797                 setEnabled(control->getValue());
00798         }
00799 }
00800 
00801 void LLMenuItemCallGL::setVisibleControl(LLString visible_control, LLView *context)
00802 {
00803         // Register new listener
00804         if (!visible_control.empty())
00805         {
00806                 LLControlVariable *control = context->findControl(visible_control);
00807                 if (!control)
00808                 {
00809                         context->addBoolControl(visible_control, getVisible());
00810                         control = context->findControl(visible_control);
00811                         llassert_always(control);
00812                 }
00813                 control->getSignal()->connect(boost::bind(&LLView::controlListener, _1, getHandle(), std::string("visible")));
00814                 setVisible(control->getValue());
00815         }
00816 }
00817 
00818 // virtual
00819 LLXMLNodePtr LLMenuItemCallGL::getXML(bool save_children) const
00820 {
00821         LLXMLNodePtr node = LLMenuItemGL::getXML();
00822 
00823         // Contents
00824 
00825         std::vector<LLListenerEntry> listeners = mDispatcher->getListeners();
00826         std::vector<LLListenerEntry>::iterator itor;
00827         for (itor = listeners.begin(); itor != listeners.end(); ++itor)
00828         {
00829                 LLString listener_name = findEventListener((LLSimpleListener*)itor->listener);
00830                 if (!listener_name.empty())
00831                 {
00832                         LLXMLNodePtr child_node = node->createChild("on_click", FALSE);
00833                         child_node->createChild("function", TRUE)->setStringValue(listener_name);
00834                         child_node->createChild("filter", TRUE)->setStringValue(itor->filter.asString());
00835                         child_node->createChild("userdata", TRUE)->setStringValue(itor->userdata.asString());
00836                 }
00837         }
00838 
00839         return node;
00840 }
00841 
00842 // doIt() - Call the callback provided
00843 void LLMenuItemCallGL::doIt( void )
00844 {
00845         // RN: menu item can be deleted in callback, so beware
00846         getMenu()->setItemLastSelected( this );
00847 
00848         if( mCallback )
00849         {
00850                 mCallback( mUserData );
00851         }
00852         LLPointer<LLEvent> fired_event = new LLEvent(this);
00853         fireEvent(fired_event, "on_click");
00854         LLMenuItemGL::doIt();
00855 }
00856 
00857 void LLMenuItemCallGL::buildDrawLabel( void )
00858 {
00859         LLPointer<LLEvent> fired_event = new LLEvent(this);
00860         fireEvent(fired_event, "on_build");
00861         if( mEnabledCallback )
00862         {
00863                 setEnabled( mEnabledCallback( mUserData ) );
00864         }
00865         if(mLabelCallback)
00866         {
00867                 LLString label;
00868                 mLabelCallback(label, mUserData);
00869                 mLabel = label;
00870         }
00871         LLMenuItemGL::buildDrawLabel();
00872 }
00873 
00874 BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
00875 {
00876         if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
00877         {
00878                 LLPointer<LLEvent> fired_event = new LLEvent(this);
00879                 fireEvent(fired_event, "on_build");
00880                 if( mEnabledCallback )
00881                 {
00882                         setEnabled( mEnabledCallback( mUserData ) );
00883                 }
00884                 if( !getEnabled() )
00885                 {
00886                         if( mOnDisabledCallback )
00887                         {
00888                                 mOnDisabledCallback( mUserData );
00889                         }
00890                 }
00891         }
00892         return LLMenuItemGL::handleAcceleratorKey(key, mask);
00893 }
00894 
00898 
00899 LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, 
00900                                                                            const LLString& label,
00901                                                                            menu_callback clicked_cb,
00902                                                                            enabled_callback enabled_cb,
00903                                                                            check_callback check_cb,
00904                                                                            void* user_data,
00905                                                                            KEY key, MASK mask ) :
00906         LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ),
00907         mCheckCallback( check_cb ), 
00908         mChecked(FALSE)
00909 {
00910 }
00911 
00912 LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, 
00913                                                                            menu_callback clicked_cb,
00914                                                                            enabled_callback enabled_cb,
00915                                                                            check_callback check_cb,
00916                                                                            void* user_data,
00917                                                                            KEY key, MASK mask ) :
00918         LLMenuItemCallGL( name, name, clicked_cb, enabled_cb, user_data, key, mask ),
00919         mCheckCallback( check_cb ), 
00920         mChecked(FALSE)
00921 {
00922 }
00923 
00924 LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name, 
00925                                                                            const LLString& label,
00926                                                                            menu_callback clicked_cb,
00927                                                                            enabled_callback enabled_cb,
00928                                                                            LLString control_name,
00929                                                                            LLView *context,
00930                                                                            void* user_data,
00931                                                                            KEY key, MASK mask ) :
00932         LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ),
00933         mCheckCallback( NULL )
00934 {
00935         setControlName(control_name, context);
00936 }
00937 
00938 //virtual
00939 void LLMenuItemCheckGL::setValue(const LLSD& value)
00940 {
00941         mChecked = value.asBoolean();
00942         if(mChecked)
00943         {
00944                 mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
00945         }
00946         else
00947         {
00948                 mDrawBoolLabel.clear();
00949         }
00950 }
00951 
00952 void LLMenuItemCheckGL::setCheckedControl(LLString checked_control, LLView *context)
00953 {
00954         // Register new listener
00955         if (!checked_control.empty())
00956         {
00957                 LLControlVariable *control = context->findControl(checked_control);
00958                 if (!control)
00959                 {
00960                         context->addBoolControl(checked_control, mChecked);
00961                         control = context->findControl(checked_control);
00962                         llassert_always(control);
00963                 }
00964                 control->getSignal()->connect(boost::bind(&LLView::controlListener, _1, getHandle(), std::string("value")));
00965                 mChecked = control->getValue();
00966         }
00967 }
00968 
00969 // virtual
00970 LLXMLNodePtr LLMenuItemCheckGL::getXML(bool save_children) const
00971 {
00972         LLXMLNodePtr node = LLMenuItemCallGL::getXML();
00973         return node;
00974 }
00975 
00976 // called to rebuild the draw label
00977 void LLMenuItemCheckGL::buildDrawLabel( void )
00978 {
00979         if(mChecked || (mCheckCallback && mCheckCallback( getUserData() ) ) )
00980         {
00981                 mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
00982         }
00983         else
00984         {
00985                 mDrawBoolLabel.clear();
00986         }
00987         LLMenuItemCallGL::buildDrawLabel();
00988 }
00989 
00990 
00994 
00995 LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, const LLString& label, BOOL* toggle,
00996                                                                                 KEY key, MASK mask ) :
00997         LLMenuItemGL( name, label, key, mask ),
00998         mToggle( toggle )
00999 {
01000 }
01001 
01002 LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, BOOL* toggle,
01003                                                                                 KEY key, MASK mask ) :
01004         LLMenuItemGL( name, name, key, mask ),
01005         mToggle( toggle )
01006 {
01007 }
01008 
01009 
01010 // called to rebuild the draw label
01011 void LLMenuItemToggleGL::buildDrawLabel( void )
01012 {
01013         if( *mToggle )
01014         {
01015                 mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
01016         }
01017         else
01018         {
01019                 mDrawBoolLabel.clear();
01020         }
01021         mDrawAccelLabel.clear();
01022         LLString st = mDrawAccelLabel;
01023         appendAcceleratorString( st );
01024         mDrawAccelLabel = st;
01025 }
01026 
01027 // doIt() - do the primary funcationality of the menu item.
01028 void LLMenuItemToggleGL::doIt( void )
01029 {
01030         getMenu()->setItemLastSelected( this );
01031         //llinfos << "LLMenuItemToggleGL::doIt " << mLabel.c_str() << llendl;
01032         *mToggle = !(*mToggle);
01033         buildDrawLabel();
01034         LLMenuItemGL::doIt();
01035 }
01036 
01037 
01038 LLMenuItemBranchGL::LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch,
01039                                                                                 KEY key, MASK mask ) :
01040         LLMenuItemGL( name, label, key, mask ),
01041         mBranch( branch )
01042 {
01043         mBranch->setVisible( FALSE );
01044         mBranch->setParentMenuItem(this);
01045 }
01046 
01047 // virtual
01048 LLView* LLMenuItemBranchGL::getChildView(const LLString& name, BOOL recurse, BOOL create_if_missing) const
01049 {
01050         // richard: this is redundant with parent, remove
01051         if (mBranch->getName() == name)
01052         {
01053                 return mBranch;
01054         }
01055         // Always recurse on branches
01056         LLView* child = mBranch->getChildView(name, recurse, FALSE);
01057         if (!child)
01058         {
01059                 child = LLView::getChildView(name, recurse, create_if_missing);
01060         }
01061         return child;
01062 }
01063 
01064 // virtual
01065 BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
01066 {
01067         // switch to mouse navigation mode
01068         LLMenuGL::setKeyboardMode(FALSE);
01069 
01070         doIt();
01071         make_ui_sound("UISndClickRelease");
01072         return TRUE;
01073 }
01074 
01075 BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
01076 {
01077         return mBranch->handleAcceleratorKey(key, mask);
01078 }
01079 
01080 // virtual
01081 LLXMLNodePtr LLMenuItemBranchGL::getXML(bool save_children) const
01082 {
01083         if (mBranch)
01084         {
01085                 return mBranch->getXML();
01086         }
01087 
01088         return LLMenuItemGL::getXML();
01089 }
01090 
01091 
01092 // This function checks to see if the accelerator key is already in use;
01093 // if not, it will be added to the list
01094 BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp)
01095 {
01096         U32 item_count = mBranch->getItemCount();
01097         LLMenuItemGL *item;
01098 
01099         while (item_count--)
01100         {
01101                 if ((item = mBranch->getItem(item_count)))
01102                 {
01103                         return item->addToAcceleratorList(listp);
01104                 }
01105         }
01106         return FALSE;
01107 }
01108 
01109 
01110 // called to rebuild the draw label
01111 void LLMenuItemBranchGL::buildDrawLabel( void )
01112 {
01113         mDrawAccelLabel.clear();
01114         LLString st = mDrawAccelLabel;
01115         appendAcceleratorString( st );
01116         mDrawAccelLabel = st;
01117         mDrawBranchLabel = BRANCH_SUFFIX;
01118 }
01119 
01120 // doIt() - do the primary functionality of the menu item.
01121 void LLMenuItemBranchGL::doIt( void )
01122 {
01123         openMenu();
01124 
01125         // keyboard navigation automatically propagates highlight to sub-menu
01126         // to facilitate fast menu control via jump keys
01127         if (LLMenuGL::getKeyboardMode() && !mBranch->getHighlightedItem())
01128         {
01129                 mBranch->highlightNextItem(NULL);
01130         }
01131 }
01132 
01133 BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
01134 {
01135         BOOL handled = FALSE;
01136         if (called_from_parent)
01137         {
01138                 handled = mBranch->handleKey(key, mask, called_from_parent);
01139         }
01140 
01141         if (!handled)
01142         {
01143                 handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
01144         }
01145 
01146         return handled;
01147 }
01148 
01149 BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
01150 {
01151         BOOL handled = FALSE;
01152         if (called_from_parent)
01153         {
01154                 handled = mBranch->handleUnicodeChar(uni_char, TRUE);
01155         }
01156 
01157         if (!handled)
01158         {
01159                 handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
01160         }
01161 
01162         return handled;
01163 }
01164 
01165 
01166 void LLMenuItemBranchGL::setHighlight( BOOL highlight )
01167 {
01168         if (highlight == getHighlight()) return;
01169 
01170         BOOL auto_open = getEnabled() && (!mBranch->getVisible() || mBranch->getTornOff());
01171         // torn off menus don't open sub menus on hover unless they have focus
01172         if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus())
01173         {
01174                 auto_open = FALSE;
01175         }
01176         // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
01177         if (mBranch->getTornOff())
01178         {
01179                 auto_open = FALSE;
01180         }
01181         LLMenuItemGL::setHighlight(highlight);
01182         if( highlight )
01183         {
01184                 if(auto_open)
01185                 {
01186                         openMenu();
01187                 }
01188         }
01189         else
01190         {
01191                 if (mBranch->getTornOff())
01192                 {
01193                         ((LLFloater*)mBranch->getParent())->setFocus(FALSE);
01194                         mBranch->clearHoverItem();
01195                 }
01196                 else
01197                 {
01198                         mBranch->setVisible( FALSE );
01199                 }
01200         }
01201 }
01202 
01203 void LLMenuItemBranchGL::draw()
01204 {
01205         LLMenuItemGL::draw();
01206         if (mBranch->getVisible() && !mBranch->getTornOff())
01207         {
01208                 setHighlight(TRUE);
01209         }
01210 }
01211 
01212 void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
01213 {
01214         if (mBranch->getParent() == NULL)
01215         {
01216                 // make the branch menu a sibling of my parent menu
01217                 mBranch->updateParent(parentp);
01218         }
01219 }
01220 
01221 void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility )
01222 {
01223         if (new_visibility == FALSE && !mBranch->getTornOff())
01224         {
01225                 mBranch->setVisible(FALSE);
01226         }
01227         LLMenuItemGL::onVisibilityChange(new_visibility);
01228 }
01229 
01230 BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
01231 {
01232         if (getMenu()->getVisible() && mBranch->getVisible() && key == KEY_LEFT)
01233         {
01234                 // switch to keyboard navigation mode
01235                 LLMenuGL::setKeyboardMode(TRUE);
01236 
01237                 BOOL handled = mBranch->clearHoverItem();
01238                 if (mBranch->getTornOff())
01239                 {
01240                         ((LLFloater*)mBranch->getParent())->setFocus(FALSE);
01241                 }
01242                 if (handled && getMenu()->getTornOff())
01243                 {
01244                         ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
01245                 }
01246                 return handled;
01247         }
01248 
01249         if (getHighlight() && 
01250                 getMenu()->isOpen() && 
01251                 key == KEY_RIGHT && !mBranch->getHighlightedItem())
01252         {
01253                 // switch to keyboard navigation mode
01254                 LLMenuGL::setKeyboardMode(TRUE);
01255 
01256                 LLMenuItemGL* itemp = mBranch->highlightNextItem(NULL);
01257                 if (itemp)
01258                 {
01259                         return TRUE;
01260                 }
01261         }
01262 
01263         return LLMenuItemGL::handleKeyHere(key, mask);
01264 }
01265 
01266 void LLMenuItemBranchGL::openMenu()
01267 {
01268         if (mBranch->getTornOff())
01269         {
01270                 gFloaterView->bringToFront((LLFloater*)mBranch->getParent());
01271                 // this might not be necessary, as torn off branches don't get focus and hence no highligth
01272                 mBranch->highlightNextItem(NULL);
01273         }
01274         else if( !mBranch->getVisible() )
01275         {
01276                 // get valid rectangle for menus
01277                 const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
01278 
01279                 mBranch->arrange();
01280 
01281                 LLRect rect = mBranch->getRect();
01282                 // calculate root-view relative position for branch menu
01283                 S32 left = getRect().mRight;
01284                 S32 top = getRect().mTop - getRect().mBottom;
01285 
01286                 localPointToOtherView(left, top, &left, &top, mBranch->getParent());
01287 
01288                 rect.setLeftTopAndSize( left, top,
01289                                                                 rect.getWidth(), rect.getHeight() );
01290 
01291                 if (mBranch->getCanTearOff())
01292                 {
01293                         rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
01294                 }
01295                 mBranch->setRect( rect );
01296                 S32 x = 0;
01297                 S32 y = 0;
01298                 mBranch->localPointToOtherView( 0, 0, &x, &y, mBranch->getParent() ); 
01299                 S32 delta_x = 0;
01300                 S32 delta_y = 0;
01301                 if( y < menu_region_rect.mBottom )
01302                 {
01303                         delta_y = menu_region_rect.mBottom - y;
01304                 }
01305 
01306                 S32 menu_region_width = menu_region_rect.getWidth();
01307                 if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() )
01308                 {
01309                         // move sub-menu over to left side
01310                         delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth())));
01311                 }
01312                 mBranch->translate( delta_x, delta_y );
01313                 mBranch->setVisible( TRUE );
01314         }
01315 }
01316 
01317 
01318 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01319 // Class LLMenuItemBranchDownGL
01320 //
01321 // The LLMenuItemBranchDownGL represents a menu item that has a
01322 // sub-menu. This is used to make menu bar menus.
01323 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01324 
01325 class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
01326 {
01327 protected:
01328 
01329 public:
01330         LLMenuItemBranchDownGL( const LLString& name, const LLString& label, LLMenuGL* branch,
01331                                                         KEY key = KEY_NONE, MASK mask = MASK_NONE );
01332 
01333         virtual LLString getType() const        { return "menu"; }
01334 
01335         // returns the normal width of this control in pixels - this is
01336         // used for calculating the widest item, as well as for horizontal
01337         // arrangement.
01338         virtual U32 getNominalWidth( void ) const;
01339 
01340         // called to rebuild the draw label
01341         virtual void buildDrawLabel( void );
01342 
01343         // handles opening, positioning, and arranging the menu branch associated with this item
01344         virtual void openMenu( void );
01345 
01346         // set the hover status (called by it's menu) and if the object is
01347         // active. This is used for behavior transfer.
01348         virtual void setHighlight( BOOL highlight );
01349 
01350         virtual BOOL isActive( void ) const;
01351 
01352         // LLView functionality
01353         virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
01354         virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); 
01355         virtual void draw( void );
01356         virtual BOOL handleKeyHere(KEY key, MASK mask);
01357         
01358         virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
01359 };
01360 
01361 LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const LLString& name,
01362                                                                                                 const LLString& label,
01363                                                                                                 LLMenuGL* branch, 
01364                                                                                                 KEY key, MASK mask ) :
01365         LLMenuItemBranchGL( name, label, branch, key, mask )
01366 {
01367 }
01368 
01369 // returns the normal width of this control in pixels - this is used
01370 // for calculating the widest item, as well as for horizontal
01371 // arrangement.
01372 U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
01373 {
01374         U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
01375         width += getFont()->getWidth( mLabel.getWString().c_str() ); 
01376         return width;
01377 }
01378 
01379 // called to rebuild the draw label
01380 void LLMenuItemBranchDownGL::buildDrawLabel( void )
01381 {
01382         mDrawAccelLabel.clear();
01383         LLString st = mDrawAccelLabel;
01384         appendAcceleratorString( st );
01385         mDrawAccelLabel = st;
01386 }
01387 
01388 void LLMenuItemBranchDownGL::openMenu( void )
01389 {
01390         LLMenuGL* branch = getBranch();
01391         if( branch->getVisible() && !branch->getTornOff() )
01392         {
01393                 branch->setVisible( FALSE );
01394         }
01395         else
01396         {
01397                 if (branch->getTornOff())
01398                 {
01399                         gFloaterView->bringToFront((LLFloater*)branch->getParent());
01400                 }
01401                 else
01402                 {
01403                         // We're showing the drop-down menu, so patch up its labels/rects
01404                         branch->arrange();
01405 
01406                         LLRect rect = branch->getRect();
01407                         S32 left = 0;
01408                         S32 top = getRect().mBottom;
01409                         localPointToOtherView(left, top, &left, &top, branch->getParent());
01410 
01411                         rect.setLeftTopAndSize( left, top,
01412                                                                         rect.getWidth(), rect.getHeight() );
01413                         branch->setRect( rect );
01414                         S32 x = 0;
01415                         S32 y = 0;
01416                         branch->localPointToScreen( 0, 0, &x, &y ); 
01417                         S32 delta_x = 0;
01418 
01419                         LLCoordScreen window_size;
01420                         LLWindow* windowp = getWindow();
01421                         windowp->getSize(&window_size);
01422 
01423                         S32 window_width = window_size.mX;
01424                         if( x > window_width - rect.getWidth() )
01425                         {
01426                                 delta_x = (window_width - rect.getWidth()) - x;
01427                         }
01428                         branch->translate( delta_x, 0 );
01429 
01430                         setHighlight(TRUE);
01431                         branch->setVisible( TRUE );
01432                 }
01433         }
01434 }
01435 
01436 // set the hover status (called by it's menu)
01437 void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
01438 {
01439         if (highlight == getHighlight()) return;
01440 
01441         //NOTE: Purposely calling all the way to the base to bypass auto-open.
01442         LLMenuItemGL::setHighlight(highlight);
01443         if( !highlight)
01444         {
01445                 if (getBranch()->getTornOff())
01446                 {
01447                         ((LLFloater*)getBranch()->getParent())->setFocus(FALSE);
01448                         getBranch()->clearHoverItem();
01449                 }
01450                 else
01451                 {
01452                         getBranch()->setVisible( FALSE );
01453                 }
01454         }
01455 }
01456 
01457 BOOL LLMenuItemBranchDownGL::isActive() const
01458 {
01459         // for top level menus, being open is sufficient to be considered 
01460         // active, because clicking on them with the mouse will open
01461         // them, without moving keyboard focus to them
01462         return isOpen();
01463 }
01464 
01465 BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
01466 {
01467         // switch to mouse control mode
01468         LLMenuGL::setKeyboardMode(FALSE);
01469         doIt();
01470         make_ui_sound("UISndClick");
01471         return TRUE;
01472 }
01473 
01474 BOOL LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
01475 {
01476         return TRUE;
01477 }
01478 
01479 
01480 BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
01481 {
01482         BOOL branch_visible = getBranch()->getVisible();
01483         BOOL handled = getBranch()->handleAcceleratorKey(key, mask);
01484         if (handled && !branch_visible && getVisible())
01485         {
01486                 // flash this menu entry because we triggered an invisible menu item
01487                 LLMenuHolderGL::setActivatedItem(this);
01488         }
01489 
01490         return handled;
01491 }
01492 
01493 BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
01494 {
01495         BOOL menu_open = getBranch()->getVisible();
01496         // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
01497         if (getHighlight() && getMenu()->getVisible() && (isActive() || LLMenuGL::getKeyboardMode()))
01498         {
01499                 if (key == KEY_LEFT)
01500                 {
01501                         // switch to keyboard navigation mode
01502                         LLMenuGL::setKeyboardMode(TRUE);
01503 
01504                         LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
01505                         // open new menu only if previous menu was open
01506                         if (itemp && itemp->getEnabled() && menu_open)
01507                         {
01508                                 itemp->doIt();
01509                         }
01510 
01511                         return TRUE;
01512                 }
01513                 else if (key == KEY_RIGHT)
01514                 {
01515                         // switch to keyboard navigation mode
01516                         LLMenuGL::setKeyboardMode(TRUE);
01517 
01518                         LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
01519                         // open new menu only if previous menu was open
01520                         if (itemp && itemp->getEnabled() && menu_open)
01521                         {
01522                                 itemp->doIt();
01523                         }
01524 
01525                         return TRUE;
01526                 }
01527                 else if (key == KEY_DOWN)
01528                 {
01529                         // switch to keyboard navigation mode
01530                         LLMenuGL::setKeyboardMode(TRUE);
01531 
01532                         if (!isActive())
01533                         {
01534                                 doIt();
01535                         }
01536                         getBranch()->highlightNextItem(NULL);
01537                         return TRUE;
01538                 }
01539                 else if (key == KEY_UP)
01540                 {
01541                         // switch to keyboard navigation mode
01542                         LLMenuGL::setKeyboardMode(TRUE);
01543 
01544                         if (!isActive())
01545                         {
01546                                 doIt();
01547                         }
01548                         getBranch()->highlightPrevItem(NULL);
01549                         return TRUE;
01550                 }
01551         }
01552 
01553         return FALSE;
01554 }
01555 
01556 void LLMenuItemBranchDownGL::draw( void )
01557 {
01558         //FIXME: try removing this
01559         if (getBranch()->getVisible() && !getBranch()->getTornOff())
01560         {
01561                 setHighlight(TRUE);
01562         }
01563 
01564         if( getHighlight() )
01565         {
01566                 gGL.color4fv( getHighlightBGColor().mV );
01567                 gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
01568         }
01569 
01570         U8 font_style = getFontStyle();
01571         if (getEnabled() && !getDrawTextDisabled() )
01572         {
01573                 font_style |= LLFontGL::DROP_SHADOW_SOFT;
01574         }
01575 
01576         LLColor4 color;
01577         if (getHighlight())
01578         {
01579                 color = getHighlightFGColor();
01580         }
01581         else if( getEnabled() )
01582         {
01583                 color = getEnabledColor();
01584         }
01585         else
01586         {
01587                 color = getDisabledColor();
01588         }
01589         getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
01590                                    LLFontGL::HCENTER, LLFontGL::BOTTOM, font_style );
01591 
01592 
01593         // underline navigation key only when keyboard navigation has been initiated
01594         if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
01595         {
01596                 LLString upper_case_label = mLabel.getString();
01597                 LLString::toUpper(upper_case_label);
01598                 std::string::size_type offset = upper_case_label.find(getJumpKey());
01599                 if (offset != std::string::npos)
01600                 {
01601                         S32 x_offset = llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
01602                         S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
01603                         S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
01604                         gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
01605                 }
01606         }
01607 
01608         // reset every frame so that we only show highlight 
01609         // when we get hover events on that frame
01610         setHover(FALSE);
01611 }
01612 
01616 
01617 static LLRegisterWidget<LLMenuGL> r1("menu");
01618 
01619 // Default constructor
01620 LLMenuGL::LLMenuGL( const LLString& name, const LLString& label, LLHandle<LLFloater> parent_floater_handle )
01621 :       LLUICtrl( name, LLRect(), FALSE, NULL, NULL ),
01622         mBackgroundColor( sDefaultBackgroundColor ),
01623         mBgVisible( TRUE ),
01624         mParentMenuItem( NULL ),
01625         mLabel( label ),
01626         mDropShadowed( TRUE ),
01627         mHorizontalLayout( FALSE ),
01628         mKeepFixedSize( FALSE ),
01629         mLastMouseX(0),
01630         mLastMouseY(0),
01631         mMouseVelX(0),
01632         mMouseVelY(0),
01633         mTornOff(FALSE),
01634         mTearOffItem(NULL),
01635         mSpilloverBranch(NULL),
01636         mSpilloverMenu(NULL),
01637         mParentFloaterHandle(parent_floater_handle),
01638         mJumpKey(KEY_NONE)
01639 {
01640         mFadeTimer.stop();
01641         setCanTearOff(TRUE, parent_floater_handle);
01642         setTabStop(FALSE);
01643 }
01644 
01645 LLMenuGL::LLMenuGL( const LLString& label, LLHandle<LLFloater> parent_floater_handle )
01646 :       LLUICtrl( label, LLRect(), FALSE, NULL, NULL ),
01647         mBackgroundColor( sDefaultBackgroundColor ),
01648         mBgVisible( TRUE ),
01649         mParentMenuItem( NULL ),
01650         mLabel( label ),
01651         mDropShadowed( TRUE ),
01652         mHorizontalLayout( FALSE ),
01653         mKeepFixedSize( FALSE ),
01654         mLastMouseX(0),
01655         mLastMouseY(0),
01656         mMouseVelX(0),
01657         mMouseVelY(0),
01658         mTornOff(FALSE),
01659         mTearOffItem(NULL),
01660         mSpilloverBranch(NULL),
01661         mSpilloverMenu(NULL),
01662         mParentFloaterHandle(parent_floater_handle),
01663         mJumpKey(KEY_NONE)
01664 {
01665         mFadeTimer.stop();
01666         setCanTearOff(TRUE, parent_floater_handle);
01667         setTabStop(FALSE);
01668 }
01669 
01670 // Destroys the object
01671 LLMenuGL::~LLMenuGL( void )
01672 {
01673         // delete the branch, as it might not be in view hierarchy
01674         // leave the menu, because it is always in view hierarchy
01675         delete mSpilloverBranch;
01676         mJumpKeys.clear();
01677 }
01678 
01679 void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle )
01680 {
01681         if (tear_off && mTearOffItem == NULL)
01682         {
01683                 mTearOffItem = new LLMenuItemTearOffGL(parent_floater_handle);
01684                 mItems.insert(mItems.begin(), mTearOffItem);
01685                 addChildAtEnd(mTearOffItem);
01686                 arrange();
01687         }
01688         else if (!tear_off && mTearOffItem != NULL)
01689         {
01690                 mItems.remove(mTearOffItem);
01691                 removeChild(mTearOffItem);
01692                 delete mTearOffItem;
01693                 mTearOffItem = NULL;
01694                 arrange();
01695         }
01696 }
01697 
01698 // virtual
01699 LLXMLNodePtr LLMenuGL::getXML(bool save_children) const
01700 {
01701         LLXMLNodePtr node = LLView::getXML();
01702 
01703         // Attributes
01704 
01705         node->createChild("opaque", TRUE)->setBoolValue(mBgVisible);
01706 
01707         node->createChild("drop_shadow", TRUE)->setBoolValue(mDropShadowed);
01708 
01709         node->createChild("tear_off", TRUE)->setBoolValue((mTearOffItem != NULL));
01710 
01711         if (mBgVisible)
01712         {
01713                 // TomY TODO: this should save out the color control name
01714                 node->createChild("color", TRUE)->setFloatValue(4, mBackgroundColor.mV);
01715         }
01716 
01717         // Contents
01718         item_list_t::const_iterator item_iter;
01719         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
01720         {
01721                 LLView* child = (*item_iter);
01722                 LLMenuItemGL* item = (LLMenuItemGL*)child;
01723 
01724                 LLXMLNodePtr child_node = item->getXML();
01725 
01726                 node->addChild(child_node);
01727         }
01728 
01729         return node;
01730 }
01731 
01732 void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory)
01733 {
01734         if (child->hasName(LL_MENU_GL_TAG))
01735         {
01736                 // SUBMENU
01737                 LLMenuGL *submenu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory);
01738                 appendMenu(submenu);
01739                 if (LLMenuGL::sMenuContainer != NULL)
01740                 {
01741                         submenu->updateParent(LLMenuGL::sMenuContainer);
01742                 }
01743                 else
01744                 {
01745                         submenu->updateParent(parent);
01746                 }
01747         }
01748         else if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || 
01749                         child->hasName(LL_MENU_ITEM_CHECK_GL_TAG) || 
01750                         child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
01751         {
01752                 LLMenuItemGL *item = NULL;
01753 
01754                 LLString type;
01755                 LLString item_name;
01756                 LLString source_label;
01757                 LLString item_label;
01758                 KEY              jump_key = KEY_NONE;
01759 
01760                 child->getAttributeString("type", type);
01761                 child->getAttributeString("name", item_name);
01762                 child->getAttributeString("label", source_label);
01763 
01764                 // parse jump key out of label
01765                 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
01766                 boost::char_separator<char> sep("_");
01767                 tokenizer tokens(source_label, sep);
01768                 tokenizer::iterator token_iter;
01769                 S32 token_count = 0;
01770                 for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
01771                 {
01772                         item_label += (*token_iter);
01773                         if (token_count > 0)
01774                         {
01775                                 jump_key = (*token_iter).c_str()[0];
01776                         }
01777                         ++token_count;
01778                 }
01779 
01780 
01781                 if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
01782                 {
01783                         appendSeparator(item_name);
01784                 }
01785                 else
01786                 {
01787                         // ITEM
01788                         if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || 
01789                                 child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
01790                         {
01791                                 MASK mask = 0;
01792                                                         
01793 #ifdef LL_DARWIN
01794                                 // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
01795                                 BOOL useMacCtrl = FALSE;
01796                                 child->getAttributeBOOL("useMacCtrl", useMacCtrl);
01797 #endif // LL_DARWIN
01798                                 
01799                                 LLString shortcut;
01800                                 child->getAttributeString("shortcut", shortcut);
01801                                 if (shortcut.find("control") != shortcut.npos)
01802                                 {
01803 #ifdef LL_DARWIN
01804                                         if ( useMacCtrl )
01805                                         {
01806                                                 mask |= MASK_MAC_CONTROL;
01807                                         }
01808 #endif // LL_DARWIN
01809                                         mask |= MASK_CONTROL;
01810                                 }
01811                                 if (shortcut.find("alt") != shortcut.npos)
01812                                 {
01813                                         mask |= MASK_ALT;
01814                                 }
01815                                 if (shortcut.find("shift") != shortcut.npos)
01816                                 {
01817                                         mask |= MASK_SHIFT;
01818                                 }
01819                                 S32 pipe_pos = shortcut.rfind("|");
01820                                 LLString key_str = shortcut.substr(pipe_pos+1);
01821 
01822                                 KEY key = KEY_NONE;
01823                                 LLKeyboard::keyFromString(key_str, &key);
01824 
01825                                 LLMenuItemCallGL *new_item;
01826                                 LLXMLNodePtr call_child;
01827 
01828                                 if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
01829                                 {
01830                                         LLString control_name;
01831                                         child->getAttributeString("control_name", control_name);
01832 
01833                                         new_item = new LLMenuItemCheckGL(item_name, item_label, 0, 0, control_name, parent, 0, key, mask);
01834 
01835                                         for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling())
01836                                         {
01837                                                 if (call_child->hasName("on_check"))
01838                                                 {
01839                                                         LLString callback_name;
01840                                                         LLString control_name = "";
01841                                                         if (call_child->hasAttribute("function"))
01842                                                         {
01843                                                                 call_child->getAttributeString("function", callback_name);
01844 
01845                                                                 control_name = callback_name;
01846 
01847                                                                 LLString callback_data = item_name;
01848                                                                 if (call_child->hasAttribute("userdata"))
01849                                                                 {
01850                                                                         call_child->getAttributeString("userdata", callback_data);
01851                                                                         if (!callback_data.empty())
01852                                                                         {
01853                                                                                 control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
01854                                                                         }
01855                                                                 }
01856 
01857                                                                 LLSD userdata;
01858                                                                 userdata["control"] = control_name;
01859                                                                 userdata["data"] = callback_data;
01860 
01861                                                                 LLSimpleListener* callback = parent->getListenerByName(callback_name);
01862 
01863                                                                 if (!callback) continue;
01864 
01865                                                                 new_item->addListener(callback, "on_build", userdata);
01866                                                         }
01867                                                         else if (call_child->hasAttribute("control"))
01868                                                         {
01869                                                                 call_child->getAttributeString("control", control_name);
01870                                                         }
01871                                                         else
01872                                                         {
01873                                                                 continue;
01874                                                         }
01875                                                         LLControlVariable *control = parent->findControl(control_name);
01876                                                         if (!control)
01877                                                         {
01878                                                                 parent->addBoolControl(control_name, FALSE);
01879                                                         }
01880                                                         ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name, parent);
01881                                                 }
01882                                         }
01883                                 }
01884                                 else
01885                                 {
01886                                         new_item = new LLMenuItemCallGL(item_name, item_label, 0, 0, 0, 0, key, mask);
01887                                 }
01888 
01889                                 for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling())
01890                                 {
01891                                         if (call_child->hasName("on_click"))
01892                                         {
01893                                                 LLString callback_name;
01894                                                 call_child->getAttributeString("function", callback_name);
01895 
01896                                                 LLString callback_data = item_name;
01897                                                 if (call_child->hasAttribute("userdata"))
01898                                                 {
01899                                                         call_child->getAttributeString("userdata", callback_data);
01900                                                 }
01901 
01902                                                 LLSimpleListener* callback = parent->getListenerByName(callback_name);
01903 
01904                                                 if (!callback) continue;
01905 
01906                                                 new_item->addListener(callback, "on_click", callback_data);
01907                                         }
01908                                         if (call_child->hasName("on_enable"))
01909                                         {
01910                                                 LLString callback_name;
01911                                                 LLString control_name = "";
01912                                                 if (call_child->hasAttribute("function"))
01913                                                 {
01914                                                         call_child->getAttributeString("function", callback_name);
01915 
01916                                                         control_name = callback_name;
01917 
01918                                                         LLString callback_data = "";
01919                                                         if (call_child->hasAttribute("userdata"))
01920                                                         {
01921                                                                 call_child->getAttributeString("userdata", callback_data);
01922                                                                 if (!callback_data.empty())
01923                                                                 {
01924                                                                         control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
01925                                                                 }
01926                                                         }
01927 
01928                                                         LLSD userdata;
01929                                                         userdata["control"] = control_name;
01930                                                         userdata["data"] = callback_data;
01931 
01932                                                         LLSimpleListener* callback = parent->getListenerByName(callback_name);
01933 
01934                                                         if (!callback) continue;
01935 
01936                                                         new_item->addListener(callback, "on_build", userdata);
01937                                                 }
01938                                                 else if (call_child->hasAttribute("control"))
01939                                                 {
01940                                                         call_child->getAttributeString("control", control_name);
01941                                                 }
01942                                                 else
01943                                                 {
01944                                                         continue;
01945                                                 }
01946                                                 new_item->setEnabledControl(control_name, parent);
01947                                         }
01948                                         if (call_child->hasName("on_visible"))
01949                                         {
01950                                                 LLString callback_name;
01951                                                 LLString control_name = "";
01952                                                 if (call_child->hasAttribute("function"))
01953                                                 {
01954                                                         call_child->getAttributeString("function", callback_name);
01955 
01956                                                         control_name = callback_name;
01957 
01958                                                         LLString callback_data = "";
01959                                                         if (call_child->hasAttribute("userdata"))
01960                                                         {
01961                                                                 call_child->getAttributeString("userdata", callback_data);
01962                                                                 if (!callback_data.empty())
01963                                                                 {
01964                                                                         control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
01965                                                                 }
01966                                                         }
01967 
01968                                                         LLSD userdata;
01969                                                         userdata["control"] = control_name;
01970                                                         userdata["data"] = callback_data;
01971 
01972                                                         LLSimpleListener* callback = parent->getListenerByName(callback_name);
01973 
01974                                                         if (!callback) continue;
01975 
01976                                                         new_item->addListener(callback, "on_build", userdata);
01977                                                 }
01978                                                 else if (call_child->hasAttribute("control"))
01979                                                 {
01980                                                         call_child->getAttributeString("control", control_name);
01981                                                 }
01982                                                 else
01983                                                 {
01984                                                         continue;
01985                                                 }
01986                                                 new_item->setVisibleControl(control_name, parent);
01987                                         }
01988                                 }
01989                                 item = new_item;
01990                                 item->setLabel(item_label);
01991                                 if (jump_key != KEY_NONE)
01992                                         item->setJumpKey(jump_key);
01993                         }
01994 
01995                         if (item != NULL)
01996                         {
01997                                 append(item);
01998                         }
01999                 }
02000         }
02001 }
02002 
02003 // are we the childmost active menu and hence our jump keys should be enabled?
02004 // or are we a free-standing torn-off menu (which uses jump keys too)
02005 BOOL LLMenuGL::jumpKeysActive()
02006 {
02007         LLMenuItemGL* highlighted_item = getHighlightedItem();
02008         BOOL active = getVisible() && getEnabled();
02009         if (getTornOff())
02010         {
02011                 // activation of jump keys on torn off menus controlled by keyboard focus
02012                 active = active && ((LLFloater*)getParent())->hasFocus();
02013 
02014         }
02015         else
02016         {
02017                 // Are we the terminal active menu?
02018                 // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
02019                 // and we don't have a highlighted menu item pointing to an active sub-menu
02020                 active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
02021                                 && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
02022         }
02023         return active;
02024 }
02025 
02026 BOOL LLMenuGL::isOpen()
02027 {
02028         if (getTornOff())
02029         {
02030                 LLMenuItemGL* itemp = getHighlightedItem();
02031                 // if we have an open sub-menu, then we are considered part of 
02032                 // the open menu chain even if we don't have focus
02033                 if (itemp && itemp->isOpen())
02034                 {
02035                         return TRUE;
02036                 }
02037                 // otherwise we are only active if we have keyboard focus
02038                 return ((LLFloater*)getParent())->hasFocus();
02039         }
02040         else
02041         {
02042                 // normally, menus are hidden as soon as the user focuses
02043                 // on another menu, so just use the visibility criterion
02044                 return getVisible();
02045         }
02046 }
02047 // static
02048 LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
02049 {
02050         LLString name("menu");
02051         node->getAttributeString("name", name);
02052 
02053         LLString label = name;
02054         node->getAttributeString("label", label);
02055 
02056         // parse jump key out of label
02057         LLString new_menu_label;
02058 
02059         typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
02060         boost::char_separator<char> sep("_");
02061         tokenizer tokens(label, sep);
02062         tokenizer::iterator token_iter;
02063 
02064         KEY jump_key = KEY_NONE;
02065         S32 token_count = 0;
02066         for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
02067         {
02068                 new_menu_label += (*token_iter);
02069                 if (token_count > 0)
02070                 {
02071                         jump_key = (*token_iter).c_str()[0];
02072                 }
02073                 ++token_count;
02074         }
02075 
02076         BOOL opaque = FALSE;
02077         node->getAttributeBOOL("opaque", opaque);
02078 
02079         LLMenuGL *menu = new LLMenuGL(name, new_menu_label);
02080 
02081         menu->setJumpKey(jump_key);
02082 
02083         BOOL tear_off = FALSE;
02084         node->getAttributeBOOL("tear_off", tear_off);
02085         menu->setCanTearOff(tear_off);
02086 
02087         if (node->hasAttribute("drop_shadow"))
02088         {
02089                 BOOL drop_shadow = FALSE;
02090                 node->getAttributeBOOL("drop_shadow", drop_shadow);
02091                 menu->setDropShadowed(drop_shadow);
02092         }
02093 
02094         menu->setBackgroundVisible(opaque);
02095         LLColor4 color(0,0,0,1);
02096         if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color))
02097         {
02098                 menu->setBackgroundColor(color);
02099         }
02100 
02101         BOOL create_jump_keys = FALSE;
02102         node->getAttributeBOOL("create_jump_keys", create_jump_keys);
02103 
02104         LLXMLNodePtr child;
02105         for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
02106         {
02107                 menu->parseChildXML(child, parent, factory);
02108         }
02109 
02110         if (create_jump_keys)
02111         {
02112                 menu->createJumpKeys();
02113         }
02114         return menu;
02115 }
02116 
02117 
02118 // rearrange the child rects so they fit the shape of the menu.
02119 void LLMenuGL::arrange( void )
02120 {
02121         // calculate the height & width, and set our rect based on that
02122         // information.
02123         const LLRect& initial_rect = getRect();
02124 
02125         U32 width = 0, height = MENU_ITEM_PADDING;
02126 
02127         cleanupSpilloverBranch();
02128 
02129         if( mItems.size() ) 
02130         {
02131                 const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
02132 
02133                 // torn off menus are not constrained to the size of the screen
02134                 U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
02135                 U32 max_height = getTornOff() ? U32_MAX : menu_region_rect.getHeight();
02136                 // *FIX: create the item first and then ask for its dimensions?
02137                 S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::sSansSerif->getWidth( "More" );
02138                 S32 spillover_item_height = llround(LLFontGL::sSansSerif->getLineHeight()) + MENU_ITEM_PADDING;
02139 
02140                 if (mHorizontalLayout)
02141                 {
02142                         item_list_t::iterator item_iter;
02143                         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02144                         {
02145                                 if ((*item_iter)->getVisible())
02146                                 {
02147                                         if (!getTornOff() && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
02148                                         {
02149                                                 // no room for any more items
02150                                                 createSpilloverBranch();
02151 
02152                                                 item_list_t::iterator spillover_iter;
02153                                                 for (spillover_iter = item_iter; spillover_iter != mItems.end(); ++spillover_iter)
02154                                                 {
02155                                                         LLMenuItemGL* itemp = (*spillover_iter);
02156                                                         removeChild(itemp);
02157                                                         mSpilloverMenu->append(itemp);
02158                                                 }
02159                                                 mItems.erase(item_iter, mItems.end());
02160                                                 
02161                                                 mItems.push_back(mSpilloverBranch);
02162                                                 addChild(mSpilloverBranch);
02163                                                 height = llmax(height, mSpilloverBranch->getNominalHeight());
02164                                                 width += mSpilloverBranch->getNominalWidth();
02165 
02166                                                 break;
02167                                         }
02168                                         else
02169                                         {
02170                                                 // track our rect
02171                                                 height = llmax(height, (*item_iter)->getNominalHeight());
02172                                                 width += (*item_iter)->getNominalWidth();
02173                                         }
02174                                 }
02175                         }
02176                 }
02177                 else
02178                 {
02179                         item_list_t::iterator item_iter;
02180                         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02181                         {
02182                                 if ((*item_iter)->getVisible())
02183                                 {
02184                                         if (!getTornOff() && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
02185                                         {
02186                                                 // no room for any more items
02187                                                 createSpilloverBranch();
02188 
02189                                                 item_list_t::iterator spillover_iter;
02190                                                 for (spillover_iter= item_iter; spillover_iter != mItems.end(); ++spillover_iter)
02191                                                 {
02192                                                         LLMenuItemGL* itemp = (*spillover_iter);
02193                                                         removeChild(itemp);
02194                                                         mSpilloverMenu->append(itemp);
02195                                                 }
02196                                                 mItems.erase(item_iter, mItems.end());
02197                                                 mItems.push_back(mSpilloverBranch);
02198                                                 addChild(mSpilloverBranch);
02199                                                 height += mSpilloverBranch->getNominalHeight();
02200                                                 width = llmax( width, mSpilloverBranch->getNominalWidth() );
02201 
02202                                                 break;
02203                                         }
02204                                         else
02205                                         {
02206                                                 // track our rect
02207                                                 height += (*item_iter)->getNominalHeight();
02208                                                 width = llmax( width, (*item_iter)->getNominalWidth() );
02209                                         }
02210                                 }
02211                         }
02212                 }
02213 
02214                 setRect(LLRect(getRect().mLeft, getRect().mBottom + height, getRect().mLeft + width, getRect().mBottom));
02215 
02216                 S32 cur_height = (S32)llmin(max_height, height);
02217                 S32 cur_width = 0;
02218                 item_list_t::iterator item_iter;
02219                 for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02220                 {
02221                         if ((*item_iter)->getVisible())
02222                         {
02223                                 // setup item rect to hold label
02224                                 LLRect rect;
02225                                 if (mHorizontalLayout)
02226                                 {
02227                                         rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
02228                                         cur_width += (*item_iter)->getNominalWidth();
02229                                 }
02230                                 else
02231                                 {
02232                                         rect.setLeftTopAndSize( 0, cur_height, width, (*item_iter)->getNominalHeight());
02233                                         cur_height -= (*item_iter)->getNominalHeight();
02234                                 }
02235                                 (*item_iter)->setRect( rect );
02236                                 (*item_iter)->buildDrawLabel();
02237                         }
02238                 }
02239         }
02240         if (mKeepFixedSize)
02241         {
02242                 reshape(initial_rect.getWidth(), initial_rect.getHeight());
02243         }
02244 }
02245 
02246 void LLMenuGL::createSpilloverBranch()
02247 {
02248         if (!mSpilloverBranch)
02249         {
02250                 // should be NULL but delete anyway
02251                 delete mSpilloverMenu;
02252                 // technically, you can't tear off spillover menus, but we're passing the handle
02253                 // along just to be safe
02254                 mSpilloverMenu = new LLMenuGL("More", "More", mParentFloaterHandle);
02255                 mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer);
02256                 // Inherit colors
02257                 mSpilloverMenu->setBackgroundColor( mBackgroundColor );
02258                 mSpilloverMenu->setCanTearOff(FALSE);
02259 
02260                 mSpilloverBranch = new LLMenuItemBranchGL("More", "More", mSpilloverMenu);
02261                 mSpilloverBranch->setFontStyle(LLFontGL::ITALIC);
02262         }
02263 }
02264 
02265 void LLMenuGL::cleanupSpilloverBranch()
02266 {
02267         if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
02268         {
02269                 // head-recursion to propagate items back up to root menu
02270                 mSpilloverMenu->cleanupSpilloverBranch();
02271 
02272                 removeChild(mSpilloverBranch);
02273 
02274                 item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), mSpilloverBranch);
02275                 if (found_iter != mItems.end())
02276                 {
02277                         mItems.erase(found_iter);
02278                 }
02279 
02280                 delete mSpilloverBranch;
02281                 mSpilloverBranch = NULL;
02282 
02283                 // pop off spillover items
02284                 while (mSpilloverMenu->getItemCount())
02285                 {
02286                         LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
02287                         mSpilloverMenu->removeChild(itemp);
02288                         mSpilloverMenu->mItems.erase(mSpilloverMenu->mItems.begin());
02289                         // put them at the end of our own list
02290                         mItems.push_back(itemp);
02291                         addChild(itemp);
02292                 }
02293         }
02294 }
02295 
02296 void LLMenuGL::createJumpKeys()
02297 {
02298         mJumpKeys.clear();
02299 
02300         std::set<LLString> unique_words;
02301         std::set<LLString> shared_words;
02302 
02303         item_list_t::iterator item_it;
02304         typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
02305         boost::char_separator<char> sep(" ");
02306 
02307         for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
02308         {
02309                 LLString uppercase_label = (*item_it)->getLabel();
02310                 LLString::toUpper(uppercase_label);
02311 
02312                 tokenizer tokens(uppercase_label, sep);
02313                 tokenizer::iterator token_iter;
02314                 for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
02315                 {
02316                         if (unique_words.find(*token_iter) != unique_words.end())
02317                         {
02318                                 // this word exists in more than one menu instance
02319                                 shared_words.insert(*token_iter);
02320                         }
02321                         else
02322                         {
02323                                 // we have a new word, keep track of it
02324                                 unique_words.insert(*token_iter);
02325                         }
02326                 }
02327         }
02328 
02329         // pre-assign specified jump keys
02330         for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
02331         {
02332                 KEY jump_key = (*item_it)->getJumpKey();
02333                 if(jump_key != KEY_NONE)
02334                 {
02335                         if (mJumpKeys.find(jump_key) == mJumpKeys.end())
02336                         {
02337                                 mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
02338                         }
02339                         else
02340                         {
02341                                 // this key is already spoken for, 
02342                                 // so we need to reassign it below
02343                                 (*item_it)->setJumpKey(KEY_NONE);
02344                         }
02345                 }
02346         }
02347 
02348         for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
02349         {
02350                 // skip over items that already have assigned jump keys
02351                 if ((*item_it)->getJumpKey() != KEY_NONE)
02352                 {
02353                         continue;
02354                 }
02355                 LLString uppercase_label = (*item_it)->getLabel();
02356                 LLString::toUpper(uppercase_label);
02357 
02358                 tokenizer tokens(uppercase_label, sep);
02359                 tokenizer::iterator token_iter;
02360 
02361                 BOOL found_key = FALSE;
02362                 for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
02363                 {
02364                         LLString uppercase_word = *token_iter;
02365 
02366                         // this word is not shared with other menu entries...
02367                         if (shared_words.find(*token_iter) == shared_words.end())
02368                         {
02369                                 S32 i;
02370                                 for(i = 0; i < (S32)uppercase_word.size(); i++)
02371                                 {
02372                                         char jump_key = uppercase_word[i];
02373                                         
02374                                         if (LLStringOps::isDigit(jump_key) || LLStringOps::isUpper(jump_key) &&
02375                                                 mJumpKeys.find(jump_key) == mJumpKeys.end())
02376                                         {
02377                                                 mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
02378                                                 (*item_it)->setJumpKey(jump_key);
02379                                                 found_key = TRUE;
02380                                                 break;
02381                                         }
02382                                 }
02383                         }
02384                         if (found_key)
02385                         {
02386                                 break;
02387                         }
02388                 }
02389         }
02390 }
02391 
02392 // remove all items on the menu
02393 void LLMenuGL::empty( void )
02394 {
02395         cleanupSpilloverBranch();
02396 
02397         mItems.clear();
02398 
02399         deleteAllChildren();
02400         
02401 }
02402 
02403 // Adjust rectangle of the menu
02404 void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
02405 {
02406         setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
02407         arrange();
02408 }
02409 
02410 BOOL LLMenuGL::handleJumpKey(KEY key)
02411 {
02412         // must perform case-insensitive comparison, so just switch to uppercase input key
02413         key = toupper(key);
02414         navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
02415         if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
02416         {
02417                 // switch to keyboard navigation mode
02418                 LLMenuGL::setKeyboardMode(TRUE);
02419 
02420                 // force highlight to close old menus and any open sub-menus
02421 
02422                 //clearHoverItem();
02423                 // force highlight to close old menus and open and sub-menus
02424                 found_it->second->setHighlight(TRUE);
02425                 found_it->second->doIt();
02426 
02427         }
02428         // if we are navigating the menus, we need to eat the keystroke
02429         // so rest of UI doesn't handle it
02430         return TRUE;
02431 }
02432 
02433 
02434 // Add the menu item to this menu.
02435 BOOL LLMenuGL::append( LLMenuItemGL* item )
02436 {
02437         mItems.push_back( item );
02438         addChild( item );
02439         arrange();
02440         return TRUE;
02441 }
02442 
02443 // add a separator to this menu
02444 BOOL LLMenuGL::appendSeparator( const LLString &separator_name )
02445 {
02446         LLMenuItemGL* separator = new LLMenuItemSeparatorGL(separator_name);
02447         return append( separator );
02448 }
02449 
02450 // add a menu - this will create a cascading menu
02451 BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
02452 {
02453         if( menu == this )
02454         {
02455                 llerrs << "** Attempt to attach menu to itself. This is certainly "
02456                            << "a logic error." << llendl;
02457         }
02458         BOOL success = TRUE;
02459 
02460         LLMenuItemBranchGL* branch = NULL;
02461         branch = new LLMenuItemBranchGL( menu->getName(), menu->getLabel(), menu );
02462         branch->setJumpKey(menu->getJumpKey());
02463         success &= append( branch );
02464 
02465         // Inherit colors
02466         menu->setBackgroundColor( mBackgroundColor );
02467 
02468         return success;
02469 }
02470 
02471 void LLMenuGL::setEnabledSubMenus(BOOL enable)
02472 {
02473         setEnabled(enable);
02474         item_list_t::iterator item_iter;
02475         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02476         {
02477                 (*item_iter)->setEnabledSubMenus( enable );
02478         }
02479 }
02480 
02481 // setItemEnabled() - pass the label and the enable flag for a menu
02482 // item. TRUE will make sure it's enabled, FALSE will disable it.
02483 void LLMenuGL::setItemEnabled( const LLString& name, BOOL enable )
02484 {
02485         item_list_t::iterator item_iter;
02486         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02487         {
02488                 if( (*item_iter)->getName() == name )
02489                 {
02490                         (*item_iter)->setEnabled( enable );
02491                         (*item_iter)->setEnabledSubMenus( enable );
02492                         break;
02493                 }
02494         }
02495 }
02496 
02497 void LLMenuGL::setItemVisible( const LLString& name, BOOL visible )
02498 {
02499         item_list_t::iterator item_iter;
02500         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02501         {
02502                 if( (*item_iter)->getName() == name )
02503                 {
02504                         (*item_iter)->setVisible( visible );
02505                         break;
02506                 }
02507         }
02508 }
02509 
02510 void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
02511 {
02512         if (getVisible())
02513         {
02514                 LLMenuHolderGL::setActivatedItem(item);
02515         }
02516 
02517         // fix the checkmarks
02518         item->buildDrawLabel();
02519 }
02520 
02521 //  Set whether drop shadowed 
02522 void LLMenuGL::setDropShadowed( const BOOL shadowed )
02523 {
02524         mDropShadowed = shadowed;
02525 }
02526 
02527 void LLMenuGL::setTornOff(BOOL torn_off)
02528 { 
02529         mTornOff = torn_off;
02530 }
02531 
02532 U32 LLMenuGL::getItemCount()
02533 {
02534         return mItems.size();
02535 }
02536 
02537 LLMenuItemGL* LLMenuGL::getItem(S32 number)
02538 {
02539         if (number >= 0 && number < (S32)mItems.size())
02540         {
02541                 item_list_t::iterator item_iter;
02542                 for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02543                 {
02544                         if (number == 0)
02545                         {
02546                                 return (*item_iter);
02547                         }
02548                         number--;
02549                 }
02550         }
02551         return NULL;
02552 }
02553 
02554 LLMenuItemGL* LLMenuGL::getHighlightedItem()
02555 {
02556         item_list_t::iterator item_iter;
02557         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02558         {
02559                 if ((*item_iter)->getHighlight())
02560                 {
02561                         return (*item_iter);
02562                 }
02563         }
02564         return NULL;
02565 }
02566 
02567 LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
02568 {
02569         // highlighting first item on a torn off menu is the
02570         // same as giving focus to it
02571         if (!cur_item && getTornOff())
02572         {
02573                 ((LLFloater*)getParent())->setFocus(TRUE);
02574         }
02575 
02576         item_list_t::iterator cur_item_iter;
02577         for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter)
02578         {
02579                 if( (*cur_item_iter) == cur_item)
02580                 {
02581                         break;
02582                 }
02583         }
02584 
02585         item_list_t::iterator next_item_iter;
02586         if (cur_item_iter == mItems.end())
02587         {
02588                 next_item_iter = mItems.begin();
02589         }
02590         else
02591         {
02592                 next_item_iter = cur_item_iter;
02593                 next_item_iter++;
02594                 if (next_item_iter == mItems.end())
02595                 {
02596                         next_item_iter = mItems.begin();
02597                 }
02598         }
02599 
02600         // when first highlighting a menu, skip over tear off menu item
02601         if (mTearOffItem && !cur_item)
02602         {
02603                 // we know the first item is the tear off menu item
02604                 cur_item_iter = mItems.begin();
02605                 next_item_iter++;
02606                 if (next_item_iter == mItems.end())
02607                 {
02608                         next_item_iter = mItems.begin();
02609                 }
02610         }
02611 
02612         while(1)
02613         {
02614                 // skip separators and disabled/invisible items
02615                 if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && (*next_item_iter)->getType() != SEPARATOR_NAME)
02616                 {
02617                         if (cur_item)
02618                         {
02619                                 cur_item->setHighlight(FALSE);
02620                         }
02621                         (*next_item_iter)->setHighlight(TRUE);
02622                         return (*next_item_iter);
02623                 }
02624 
02625 
02626                 if (!skip_disabled || next_item_iter == cur_item_iter)
02627                 {
02628                         break;
02629                 }
02630 
02631                 next_item_iter++;
02632                 if (next_item_iter == mItems.end())
02633                 {
02634                         if (cur_item_iter == mItems.end())
02635                         {
02636                                 break;
02637                         }
02638                         next_item_iter = mItems.begin();
02639                 }
02640         }
02641 
02642         return NULL;
02643 }
02644 
02645 LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
02646 {
02647         // highlighting first item on a torn off menu is the
02648         // same as giving focus to it
02649         if (!cur_item && getTornOff())
02650         {
02651                 ((LLFloater*)getParent())->setFocus(TRUE);
02652         }
02653 
02654         item_list_t::reverse_iterator cur_item_iter;
02655         for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter)
02656         {
02657                 if( (*cur_item_iter) == cur_item)
02658                 {
02659                         break;
02660                 }
02661         }
02662 
02663         item_list_t::reverse_iterator prev_item_iter;
02664         if (cur_item_iter == mItems.rend())
02665         {
02666                 prev_item_iter = mItems.rbegin();
02667         }
02668         else
02669         {
02670                 prev_item_iter = cur_item_iter;
02671                 prev_item_iter++;
02672                 if (prev_item_iter == mItems.rend())
02673                 {
02674                         prev_item_iter = mItems.rbegin();
02675                 }
02676         }
02677 
02678         while(1)
02679         {
02680                 // skip separators and disabled/invisible items
02681                 if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
02682                 {
02683                         (*prev_item_iter)->setHighlight(TRUE);
02684                         return (*prev_item_iter);
02685                 }
02686 
02687                 if (!skip_disabled || prev_item_iter == cur_item_iter)
02688                 {
02689                         break;
02690                 }
02691 
02692                 prev_item_iter++;
02693                 if (prev_item_iter == mItems.rend())
02694                 {
02695                         if (cur_item_iter == mItems.rend())
02696                         {
02697                                 break;
02698                         }
02699 
02700                         prev_item_iter = mItems.rbegin();
02701                 }
02702         }
02703 
02704         return NULL;
02705 }
02706 
02707 void LLMenuGL::buildDrawLabels()
02708 {
02709         item_list_t::iterator item_iter;
02710         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02711         {
02712                 (*item_iter)->buildDrawLabel();
02713         }
02714 }
02715 
02716 void LLMenuGL::updateParent(LLView* parentp)
02717 {
02718         if (getParent())
02719         {
02720                 getParent()->removeChild(this);
02721         }
02722         parentp->addChild(this);
02723         item_list_t::iterator item_iter;
02724         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02725         {
02726                 (*item_iter)->updateBranchParent(parentp);
02727         }
02728 }
02729 
02730 BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
02731 {
02732         // don't handle if not enabled
02733         if(!getEnabled())
02734         {
02735                 return FALSE;
02736         }
02737 
02738         // Pass down even if not visible
02739         item_list_t::iterator item_iter;
02740         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
02741         {
02742                 LLMenuItemGL* itemp = *item_iter;
02743                 if (itemp->handleAcceleratorKey(key, mask))
02744                 {
02745                         return TRUE;
02746                 }
02747         }
02748 
02749         return FALSE;
02750 }
02751 
02752 BOOL LLMenuGL::handleUnicodeCharHere( llwchar uni_char )
02753 {
02754         if (jumpKeysActive())
02755         {
02756                 return handleJumpKey((KEY)uni_char);
02757         }
02758         return FALSE;
02759 }
02760 
02761 BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
02762 {
02763         // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
02764         BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
02765         S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
02766         S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
02767         LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
02768         mouse_dir.normVec();
02769         LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY);
02770         mouse_avg_dir.normVec();
02771         F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f));
02772         mMouseVelX = llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
02773         mMouseVelY = llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
02774         mLastMouseX = x;
02775         mLastMouseY = y;
02776 
02777         // don't change menu focus unless mouse is moving or alt key is not held down
02778         if ((llabs(mMouseVelX) > 0 || 
02779                         llabs(mMouseVelY) > 0) &&
02780                 (!mHasSelection ||
02781                 //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
02782                 (mMouseVelX < 0) ||
02783                 llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
02784         {
02785                 for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02786                 {
02787                         LLView* viewp = *child_it;
02788                         S32 local_x = x - viewp->getRect().mLeft;
02789                         S32 local_y = y - viewp->getRect().mBottom;
02790                         if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
02791                         {
02792                                 // moving mouse always highlights new item
02793                                 if (mouse_delta_x != 0 || mouse_delta_y != 0)
02794                                 {
02795                                         ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
02796                                 }
02797                         }
02798                 }
02799 
02800                 for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02801                 {
02802                         LLView* viewp = *child_it;
02803                         S32 local_x = x - viewp->getRect().mLeft;
02804                         S32 local_y = y - viewp->getRect().mBottom;
02805                         //RN: always call handleHover to track mGotHover status
02806                         // but only set highlight when mouse is moving
02807                         if( viewp->getVisible() && 
02808                                 //RN: allow disabled items to be highlighted to preserve "active" menus when
02809                                 // moving mouse through them
02810                                 //viewp->getEnabled() && 
02811                                 viewp->pointInView(local_x, local_y) && 
02812                                 viewp->handleHover(local_x, local_y, mask))
02813                         {
02814                                 // moving mouse always highlights new item
02815                                 if (mouse_delta_x != 0 || mouse_delta_y != 0)
02816                                 {
02817                                         ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
02818                                         LLMenuGL::setKeyboardMode(FALSE);
02819                                 }
02820                                 mHasSelection = TRUE;
02821                         }
02822                 }
02823         }
02824         getWindow()->setCursor(UI_CURSOR_ARROW);
02825         return TRUE;
02826 }
02827 
02828 void LLMenuGL::draw( void )
02829 {
02830         if (mDropShadowed && !mTornOff)
02831         {
02832                 gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, 
02833                         LLUI::sColorsGroup->getColor("ColorDropShadow"), 
02834                         LLUI::sConfigGroup->getS32("DropShadowFloater") );
02835         }
02836 
02837         LLColor4 bg_color = mBackgroundColor;
02838 
02839         if( mBgVisible )
02840         {
02841                 gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor );
02842         }
02843         LLView::draw();
02844 }
02845 
02846 void LLMenuGL::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
02847 {
02848         gGL.color4fv( color.mV );
02849         LLRect item_rect = itemp->getRect();
02850         gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
02851 }
02852 
02853 void LLMenuGL::setVisible(BOOL visible)
02854 {
02855         if (visible != getVisible())
02856         {
02857                 if (!visible)
02858                 {
02859                         mFadeTimer.start();
02860                         clearHoverItem();
02861                         // reset last known mouse coordinates so
02862                         // we don't spoof a mouse move next time we're opened
02863                         mLastMouseX = 0;
02864                         mLastMouseY = 0;
02865                 }
02866                 else
02867                 {
02868                         mHasSelection = FALSE;
02869                         mFadeTimer.stop();
02870                 }
02871 
02872                 LLView::setVisible(visible);
02873         }
02874 }
02875 
02876 LLMenuGL* LLMenuGL::getChildMenuByName(const LLString& name, BOOL recurse) const
02877 {
02878         LLView* view = getChildView(name, recurse, FALSE);
02879         if (view)
02880         {
02881                 LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
02882                 if (branch)
02883                 {
02884                         return branch->getBranch();
02885                 }
02886 
02887                 LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
02888                 if (menup)
02889                 {
02890                         return menup;
02891                 }
02892         }
02893         llwarns << "Child Menu " << name << " not found in menu " << getName() << llendl;
02894         return NULL;
02895 }
02896 
02897 BOOL LLMenuGL::clearHoverItem()
02898 {
02899         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02900         {
02901                 LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
02902                 if (itemp->getHighlight())
02903                 {
02904                         itemp->setHighlight(FALSE);
02905                         return TRUE;
02906                 }
02907         }               
02908         return FALSE;
02909 }
02910 
02911 void hide_top_view( LLView* view )
02912 {
02913         if( view ) view->setVisible( FALSE );
02914 }
02915 
02916 
02917 // static
02918 void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
02919 {
02920         const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
02921 
02922         const S32 HPAD = 2;
02923         LLRect rect = menu->getRect();
02924         //LLView* cur_view = spawning_view;
02925         S32 left = x + HPAD;
02926         S32 top = y;
02927         spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
02928         rect.setLeftTopAndSize( left, top,
02929                                                         rect.getWidth(), rect.getHeight() );
02930 
02931 
02932         //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight());
02933         menu->setRect( rect );
02934 
02935         S32 bottom;
02936         left = rect.mLeft;
02937         bottom = rect.mBottom;
02938         //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom, 
02939         //                                                                      &left, &bottom ); 
02940         S32 delta_x = 0;
02941         S32 delta_y = 0;
02942         if( bottom < menu_region_rect.mBottom )
02943         {
02944                 // At this point, we need to move the context menu to the
02945                 // other side of the mouse.
02946                 //delta_y = menu_region_rect.mBottom - bottom;
02947                 delta_y = (rect.getHeight() + 2 * HPAD);
02948         }
02949 
02950         if( left > menu_region_rect.mRight - rect.getWidth() )
02951         {
02952                 // At this point, we need to move the context menu to the
02953                 // other side of the mouse.
02954                 //delta_x = (window_width - rect.getWidth()) - x;
02955                 delta_x = -(rect.getWidth() + 2 * HPAD);
02956         }
02957         menu->translate( delta_x, delta_y );
02958         menu->setVisible( TRUE );
02959 }
02960 
02961 //-----------------------------------------------------------------------------
02962 // class LLPieMenuBranch
02963 // A branch to another pie menu
02964 //-----------------------------------------------------------------------------
02965 class LLPieMenuBranch : public LLMenuItemGL
02966 {
02967 public:
02968         LLPieMenuBranch(const LLString& name, const LLString& label, LLPieMenu* branch);
02969 
02970         // called to rebuild the draw label
02971         virtual void buildDrawLabel( void );
02972 
02973         // doIt() - do the primary funcationality of the menu item.
02974         virtual void doIt( void );
02975 
02976         LLPieMenu* getBranch() { return mBranch; }
02977 
02978 protected:
02979         LLPieMenu* mBranch;
02980 };
02981 
02982 LLPieMenuBranch::LLPieMenuBranch(const LLString& name,
02983                                                                  const LLString& label,
02984                                                                  LLPieMenu* branch) 
02985 :       LLMenuItemGL( name, label, KEY_NONE, MASK_NONE ),
02986         mBranch( branch )
02987 {
02988         mBranch->hide(FALSE);
02989         mBranch->setParentMenuItem(this);
02990 }
02991 
02992 // called to rebuild the draw label
02993 void LLPieMenuBranch::buildDrawLabel( void )
02994 {
02995         {
02996                 // default enablement is this -- if any of the subitems are
02997                 // enabled, this item is enabled. JC
02998                 U32 sub_count = mBranch->getItemCount();
02999                 U32 i;
03000                 BOOL any_enabled = FALSE;
03001                 for (i = 0; i < sub_count; i++)
03002                 {
03003                         LLMenuItemGL* item = mBranch->getItem(i);
03004                         item->buildDrawLabel();
03005                         if (item->getEnabled() && !item->getDrawTextDisabled() )
03006                         {
03007                                 any_enabled = TRUE;
03008                                 break;
03009                         }
03010                 }
03011                 setDrawTextDisabled(!any_enabled);
03012                 setEnabled(TRUE);
03013         }
03014 
03015         mDrawAccelLabel.clear();
03016         LLString st = mDrawAccelLabel;
03017         appendAcceleratorString( st );
03018         mDrawAccelLabel = st;
03019         
03020         // No special branch suffix
03021         mDrawBranchLabel.clear();
03022 }
03023 
03024 // doIt() - do the primary funcationality of the menu item.
03025 void LLPieMenuBranch::doIt( void )
03026 {
03027         LLPieMenu *parent = (LLPieMenu *)getParent();
03028 
03029         LLRect rect = parent->getRect();
03030         S32 center_x;
03031         S32 center_y;
03032         parent->localPointToScreen(rect.getWidth() / 2, rect.getHeight() / 2, &center_x, &center_y);
03033 
03034         parent->hide(FALSE);
03035         mBranch->show(  center_x, center_y, FALSE );
03036 }
03037 
03038 //-----------------------------------------------------------------------------
03039 // class LLPieMenu
03040 // A circular menu of items, icons, etc.
03041 //-----------------------------------------------------------------------------
03042 LLPieMenu::LLPieMenu(const LLString& name, const LLString& label)
03043 :       LLMenuGL(name, label),
03044         mFirstMouseDown(FALSE),
03045         mUseInfiniteRadius(FALSE),
03046         mHoverItem(NULL),
03047         mHoverThisFrame(FALSE),
03048         mHoveredAnyItem(FALSE),
03049         mOuterRingAlpha(1.f),
03050         mCurRadius(0.f),
03051         mRightMouseDown(FALSE)
03052 { 
03053         LLMenuGL::setVisible(FALSE);
03054         setCanTearOff(FALSE);
03055 }
03056 
03057 LLPieMenu::LLPieMenu(const LLString& name)
03058 :       LLMenuGL(name, name),
03059         mFirstMouseDown(FALSE),
03060         mUseInfiniteRadius(FALSE),
03061         mHoverItem(NULL),
03062         mHoverThisFrame(FALSE),
03063         mHoveredAnyItem(FALSE),
03064         mOuterRingAlpha(1.f),
03065         mCurRadius(0.f),
03066         mRightMouseDown(FALSE)
03067 { 
03068         LLMenuGL::setVisible(FALSE);
03069         setCanTearOff(FALSE);
03070 }
03071 
03072 
03073 void LLPieMenu::initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *factory)
03074 {
03075         LLXMLNodePtr child;
03076         for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
03077         {
03078                 if (child->hasName(LL_PIE_MENU_TAG))
03079                 {
03080                         // SUBMENU
03081                         LLString name("menu");
03082                         child->getAttributeString("name", name);
03083                         LLString label(name);
03084                         child->getAttributeString("label", label);
03085 
03086                         LLPieMenu *submenu = new LLPieMenu(name, label);
03087                         appendPieMenu(submenu);
03088                         submenu->initXML(child, context, factory);
03089                 }
03090                 else
03091                 {
03092                         parseChildXML(child, context, factory);
03093                 }
03094         }
03095 }
03096 
03097 // virtual
03098 void LLPieMenu::setVisible(BOOL visible)
03099 {
03100         if (!visible)
03101         {
03102                 hide(FALSE);
03103         }
03104 }
03105 
03106 BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask )
03107 {
03108         // This is mostly copied from the llview class, but it continues
03109         // the hover handle code after a hover handler has been found.
03110         BOOL handled = FALSE;
03111 
03112         // If we got a hover event, we've already moved the cursor
03113         // for any menu shifts, so subsequent mouseup messages will be in the
03114         // correct position.  No need to correct them.
03115         //mShiftHoriz = 0;
03116         //mShiftVert = 0;
03117 
03118         // release mouse capture after short period of visibility if we're using a finite boundary
03119         // so that right click outside of boundary will trigger new pie menu
03120         if (hasMouseCapture() && 
03121                 !mRightMouseDown && 
03122                 mShrinkBorderTimer.getStarted() && 
03123                 mShrinkBorderTimer.getElapsedTimeF32() >= PIE_SHRINK_TIME)
03124         {
03125                 gFocusMgr.setMouseCapture(NULL);
03126                 mUseInfiniteRadius = FALSE;
03127         }
03128 
03129         LLMenuItemGL *item = pieItemFromXY( x, y );
03130 
03131         if (item && item->getEnabled())
03132         {
03133                 getWindow()->setCursor(UI_CURSOR_ARROW);
03134                 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;              
03135                 handled = TRUE;
03136 
03137                 if (item != mHoverItem)
03138                 {
03139                         if (mHoverItem)
03140                         {
03141                                 mHoverItem->setHighlight( FALSE );
03142                         }
03143                         mHoverItem = item;
03144                         mHoverItem->setHighlight( TRUE );
03145 
03146                         switch(pieItemIndexFromXY(x, y))
03147                         {
03148                         case 0:
03149                                 make_ui_sound("UISndPieMenuSliceHighlight0");
03150                                 break;
03151                         case 1:
03152                                 make_ui_sound("UISndPieMenuSliceHighlight1");
03153                                 break;
03154                         case 2:
03155                                 make_ui_sound("UISndPieMenuSliceHighlight2");
03156                                 break;
03157                         case 3:
03158                                 make_ui_sound("UISndPieMenuSliceHighlight3");
03159                                 break;
03160                         case 4:
03161                                 make_ui_sound("UISndPieMenuSliceHighlight4");
03162                                 break;
03163                         case 5:
03164                                 make_ui_sound("UISndPieMenuSliceHighlight5");
03165                                 break;
03166                         case 6:
03167                                 make_ui_sound("UISndPieMenuSliceHighlight6");
03168                                 break;
03169                         case 7:
03170                                 make_ui_sound("UISndPieMenuSliceHighlight7");
03171                                 break;
03172                         default:
03173                                 make_ui_sound("UISndPieMenuSliceHighlight0");
03174                                 break;
03175                         }
03176                 }
03177                 mHoveredAnyItem = TRUE;
03178         }
03179         else
03180         {
03181                 // clear out our selection
03182                 if (mHoverItem)
03183                 {
03184                         mHoverItem->setHighlight(FALSE);
03185                         mHoverItem = NULL;
03186                 }
03187         }
03188 
03189         if( !handled && pointInView( x, y ) )
03190         {
03191                 getWindow()->setCursor(UI_CURSOR_ARROW);
03192                 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;              
03193                 handled = TRUE;
03194         }
03195 
03196         mHoverThisFrame = TRUE;
03197 
03198         return handled;
03199 }
03200 
03201 BOOL LLPieMenu::handleMouseDown( S32 x, S32 y, MASK mask )
03202 {
03203         BOOL handled = FALSE;
03204         // The click was somewhere within our rectangle
03205         LLMenuItemGL *item = pieItemFromXY( x, y );
03206 
03207         if (item)
03208         {
03209                 // lie to the item about where the click happened
03210                 // to make sure it's within the item's rectangle
03211                 handled = item->handleMouseDown( 0, 0, mask );
03212         }
03213         else if (!mRightMouseDown)
03214         {
03215                 // call hidemenus to make sure transient selections get cleared
03216                 ((LLMenuHolderGL*)getParent())->hideMenus();
03217         }
03218 
03219         // always handle mouse down as mouse up will close open menus
03220         return handled;
03221 }
03222 
03223 BOOL LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
03224 {
03225         BOOL handled = FALSE;
03226 
03227         mRightMouseDown = TRUE;
03228 
03229         // The click was somewhere within our rectangle
03230         LLMenuItemGL *item = pieItemFromXY( x, y );
03231         S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX();
03232         S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY();
03233         BOOL clicked_in_pie = ((delta_x * delta_x) + (delta_y * delta_y) < mCurRadius*mCurRadius) || mUseInfiniteRadius;
03234 
03235         // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
03236         if (clicked_in_pie)
03237         {
03238                 // capture mouse cursor as if on initial menu show
03239                 gFocusMgr.setMouseCapture(this);
03240                 mShrinkBorderTimer.stop();
03241                 mUseInfiniteRadius = TRUE;
03242                 handled = TRUE;
03243         }
03244         
03245         if (item)
03246         {
03247                 // lie to the item about where the click happened
03248                 // to make sure it's within the item's rectangle
03249                 if (item->handleMouseDown( 0, 0, mask ))
03250                 {
03251                         handled = TRUE;
03252                 }
03253         }
03254 
03255         return handled;
03256 }
03257 
03258 BOOL LLPieMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
03259 {
03260         // release mouse capture when right mouse button released, and we're past the shrink time
03261         if (mShrinkBorderTimer.getStarted() && 
03262                 mShrinkBorderTimer.getElapsedTimeF32() > PIE_SHRINK_TIME)
03263         {
03264                 mUseInfiniteRadius = FALSE;
03265                 gFocusMgr.setMouseCapture(NULL);
03266         }
03267 
03268         S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX();
03269         S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY();
03270         if (!mHoveredAnyItem && !mFirstMouseDown && (delta_x * delta_x) + (delta_y * delta_y) < PIE_CENTER_SIZE * PIE_CENTER_SIZE)
03271         {
03272                 // user released right mouse button in middle of pie, interpret this as closing the menu
03273                 sMenuContainer->hideMenus();
03274                 return TRUE;
03275         }
03276 
03277 
03278         BOOL result = handleMouseUp( x, y, mask );
03279         mRightMouseDown = FALSE;
03280         mHoveredAnyItem = FALSE;
03281 
03282         return result;
03283 }
03284 
03285 BOOL LLPieMenu::handleMouseUp( S32 x, S32 y, MASK mask )
03286 {
03287         BOOL handled = FALSE;
03288 
03289         // The click was somewhere within our rectangle
03290         LLMenuItemGL *item = pieItemFromXY( x, y );
03291 
03292         if (item)
03293         {
03294                 // lie to the item about where the click happened
03295                 // to make sure it's within the item's rectangle
03296                 if (item->getEnabled())
03297                 {
03298                         handled = item->handleMouseUp( 0, 0, mask );
03299                         hide(TRUE);
03300                 }
03301         }
03302         else if (!mRightMouseDown)
03303         {
03304                 // call hidemenus to make sure transient selections get cleared
03305                 ((LLMenuHolderGL*)getParent())->hideMenus();
03306         }
03307 
03308         if (handled)
03309         {
03310                 make_ui_sound("UISndClickRelease");
03311         }
03312 
03313         if (!handled && !mUseInfiniteRadius)
03314         {
03315                 // call hidemenus to make sure transient selections get cleared
03316                 sMenuContainer->hideMenus();
03317         }
03318 
03319         if (mFirstMouseDown)
03320         {
03321                 make_ui_sound("UISndPieMenuAppear");
03322                 mFirstMouseDown = FALSE;
03323         }
03324         
03325         // *FIX: is this necessary?
03326         if (!mShrinkBorderTimer.getStarted())
03327         {
03328                 mShrinkBorderTimer.start();
03329         }
03330 
03331         return handled;
03332 }
03333 
03334 
03335 // virtual
03336 void LLPieMenu::draw()
03337 {
03338         // clear hover if mouse moved away
03339         if (!mHoverThisFrame && mHoverItem)
03340         {
03341                 mHoverItem->setHighlight(FALSE);
03342                 mHoverItem = NULL;
03343         }
03344 
03345         F32 width = (F32) getRect().getWidth();
03346         F32 height = (F32) getRect().getHeight();
03347         mCurRadius = PIE_SCALE_FACTOR * llmax( width/2, height/2 );
03348 
03349         mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f;
03350         if (mShrinkBorderTimer.getStarted())
03351         {
03352                 mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 0.f, 1.f);
03353                 mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR);
03354         }
03355 
03356         // correct for non-square pixels
03357         F32 center_x = width/2;
03358         F32 center_y = height/2;
03359         S32 steps = 100;
03360 
03361         gGL.pushMatrix();
03362         {
03363                 gGL.translatef(center_x, center_y, 0.f);
03364 
03365                 F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth");
03366                 LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor");
03367                 LLColor4 bg_color = LLUI::sColorsGroup->getColor("PieMenuBgColor");
03368                 LLColor4 selected_color = LLUI::sColorsGroup->getColor("PieMenuSelectedColor");
03369 
03370                 // main body
03371                 LLColor4 outer_color = bg_color;
03372                 outer_color.mV[VALPHA] *= mOuterRingAlpha;
03373                 gl_washer_2d( mCurRadius, (F32) PIE_CENTER_SIZE, steps, bg_color, outer_color );
03374 
03375                 // selected wedge
03376                 item_list_t::iterator item_iter;
03377                 S32 i = 0;
03378                 for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03379                 {
03380                         if ((*item_iter)->getHighlight())
03381                         {
03382                                 F32 arc_size = F_PI * 0.25f;
03383 
03384                                 F32 start_radians = (i * arc_size) - (arc_size * 0.5f);
03385                                 F32 end_radians = start_radians + arc_size;
03386 
03387                                 LLColor4 outer_color = selected_color;
03388                                 outer_color.mV[VALPHA] *= mOuterRingAlpha;
03389                                 gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, selected_color, outer_color );
03390                         }
03391                         i++;
03392                 }
03393 
03394                 LLUI::setLineWidth( line_width );
03395 
03396                 // inner lines
03397                 outer_color = line_color;
03398                 outer_color.mV[VALPHA] *= mOuterRingAlpha;
03399                 gl_washer_spokes_2d( mCurRadius, (F32)PIE_CENTER_SIZE, 8, line_color, outer_color );
03400 
03401                 // inner circle
03402                 gGL.color4fv( line_color.mV );
03403                 gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE );
03404 
03405                 // outer circle
03406                 gGL.color4fv( outer_color.mV );
03407                 gl_circle_2d( 0, 0, mCurRadius, steps, FALSE );
03408 
03409                 LLUI::setLineWidth(1.0f);
03410         }
03411         gGL.popMatrix();
03412 
03413         mHoverThisFrame = FALSE;
03414 
03415         LLView::draw();
03416 }
03417 
03418 void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
03419 {
03420         F32 width = (F32) getRect().getWidth();
03421         F32 height = (F32) getRect().getHeight();
03422         F32 center_x = width/2;
03423         F32 center_y = height/2;
03424         S32 steps = 100;
03425 
03426         gGL.color4fv( color.mV );
03427         gGL.pushMatrix();
03428         {
03429                 gGL.translatef(center_x - itemp->getRect().mLeft, center_y - itemp->getRect().mBottom, 0.f);
03430 
03431                 item_list_t::iterator item_iter;
03432                 S32 i = 0;
03433                 for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03434                 {
03435                         if ((*item_iter) == itemp)
03436                         {
03437                                 F32 arc_size = F_PI * 0.25f;
03438 
03439                                 F32 start_radians = (i * arc_size) - (arc_size * 0.5f);
03440                                 F32 end_radians = start_radians + arc_size;
03441 
03442                                 LLColor4 outer_color = color;
03443                                 outer_color.mV[VALPHA] *= mOuterRingAlpha;
03444                                 gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, color, outer_color );
03445                         }
03446                         i++;
03447                 }
03448         }
03449         gGL.popMatrix();
03450 }
03451 
03452 // virtual
03453 BOOL LLPieMenu::append(LLMenuItemGL *item)
03454 {
03455         item->setBriefItem(TRUE);
03456         item->setFont( LLFontGL::sSansSerifSmall );
03457         return LLMenuGL::append(item);
03458 }
03459 
03460 // virtual
03461 BOOL LLPieMenu::appendSeparator(const LLString &separator_name)
03462 {
03463         LLMenuItemGL* separator = new LLMenuItemBlankGL();
03464         separator->setFont( LLFontGL::sSansSerifSmall );
03465         return append( separator );
03466 }
03467 
03468 
03469 BOOL LLPieMenu::appendPieMenu(LLPieMenu *menu)
03470 {
03471         if (menu == this)
03472         {
03473                 llerrs << "Can't attach a pie menu to itself" << llendl;
03474         }
03475         LLPieMenuBranch *item;
03476         item = new LLPieMenuBranch(menu->getName(), menu->getLabel(), menu);
03477         getParent()->addChild(item->getBranch());
03478         item->setFont( LLFontGL::sSansSerifSmall );
03479         return append( item );
03480 }
03481 
03482 // virtual
03483 void LLPieMenu::arrange()
03484 {
03485         const S32 rect_height = 180;
03486         const S32 rect_width = 180;
03487 
03488         // all divide by 6
03489         const S32 CARD_X = 60;
03490         const S32 DIAG_X = 48;
03491         const S32 CARD_Y = 76;
03492         const S32 DIAG_Y = 42;
03493 
03494         const S32 ITEM_CENTER_X[] = { CARD_X, DIAG_X,      0, -DIAG_X, -CARD_X, -DIAG_X,       0,  DIAG_X };
03495         const S32 ITEM_CENTER_Y[] = {      0, DIAG_Y, CARD_Y,  DIAG_Y,       0, -DIAG_Y, -CARD_Y, -DIAG_Y };
03496 
03497         LLRect rect;
03498         
03499         S32 font_height = 0;
03500         if( mItems.size() )
03501         {
03502                 font_height = (*mItems.begin())->getNominalHeight();
03503         }
03504         S32 item_width = 0;
03505 
03506 //      F32 sin_delta = OO_SQRT2;       // sin(45 deg)
03507 //      F32 cos_delta = OO_SQRT2;       // cos(45 deg)
03508 
03509         // TODO: Compute actual bounding rect for menu
03510 
03511         // HACK: casting away const.  Should use setRect or some helper function instead.
03512         const_cast<LLRect&>(getRect()).setOriginAndSize(getRect().mLeft, getRect().mBottom, rect_width, rect_height );
03513 
03514         // place items around a circle, with item 0 at positive X,
03515         // rotating counter-clockwise
03516         item_list_t::iterator item_iter;
03517         S32 i = 0;
03518         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03519         {
03520                 LLMenuItemGL *item = *item_iter;
03521 
03522                 item_width = item->getNominalWidth();
03523 
03524                 // Put in the right place around a circle centered at 0,0
03525                 rect.setCenterAndSize(ITEM_CENTER_X[i],
03526                                                           ITEM_CENTER_Y[i], 
03527                                                           item_width, font_height );
03528 
03529                 // Correct for the actual rectangle size
03530                 rect.translate( rect_width/2, rect_height/2 );
03531 
03532                 item->setRect( rect );
03533 
03534                 // Make sure enablement is correct
03535                 item->buildDrawLabel();
03536                 i++;
03537         }
03538 }
03539 
03540 LLMenuItemGL *LLPieMenu::pieItemFromXY(S32 x, S32 y)
03541 {
03542         // We might have shifted this menu on draw.  If so, we need
03543         // to shift over mouseup events until we get a hover event.
03544         //x += mShiftHoriz;
03545         //y += mShiftVert; 
03546 
03547         // An arc of the pie menu is 45 degrees
03548         const F32 ARC_DEG = 45.f;
03549         S32 delta_x = x - getRect().getWidth() / 2;
03550         S32 delta_y = y - getRect().getHeight() / 2;
03551 
03552         // circle safe zone in the center
03553         S32 dist_squared = delta_x*delta_x + delta_y*delta_y;
03554         if (dist_squared < PIE_CENTER_SIZE*PIE_CENTER_SIZE)
03555         {
03556                 return NULL;
03557         }
03558 
03559         // infinite radius is only used with right clicks
03560         S32 radius = llmax( getRect().getWidth()/2, getRect().getHeight()/2 );
03561         if (!(mUseInfiniteRadius && mRightMouseDown) && dist_squared > radius * radius)
03562         {
03563                 return NULL;
03564         }
03565 
03566         F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x);
03567         
03568         // rotate marks CCW so that east = [0, ARC_DEG) instead of
03569         // [-ARC_DEG/2, ARC_DEG/2)
03570         angle += ARC_DEG / 2.f;
03571 
03572         // make sure we're only using positive angles
03573         if (angle < 0.f) angle += 360.f;
03574 
03575         S32 which = S32( angle / ARC_DEG );
03576 
03577         if (0 <= which && which < (S32)mItems.size() )
03578         {
03579                 item_list_t::iterator item_iter;
03580                 for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03581                 {
03582                         if (which == 0)
03583                         {
03584                                 return (*item_iter);
03585                         }
03586                         which--;
03587                 }
03588         }
03589 
03590         return NULL;
03591 }
03592 
03593 S32 LLPieMenu::pieItemIndexFromXY(S32 x, S32 y)
03594 {
03595         // An arc of the pie menu is 45 degrees
03596         const F32 ARC_DEG = 45.f;
03597         // correct for non-square pixels
03598         S32 delta_x = x - getRect().getWidth() / 2;
03599         S32 delta_y = y - getRect().getHeight() / 2;
03600 
03601         // circle safe zone in the center
03602         if (delta_x*delta_x + delta_y*delta_y < PIE_CENTER_SIZE*PIE_CENTER_SIZE)
03603         {
03604                 return -1;
03605         }
03606 
03607         F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x);
03608         
03609         // rotate marks CCW so that east = [0, ARC_DEG) instead of
03610         // [-ARC_DEG/2, ARC_DEG/2)
03611         angle += ARC_DEG / 2.f;
03612 
03613         // make sure we're only using positive angles
03614         if (angle < 0.f) angle += 360.f;
03615 
03616         S32 which = S32( angle / ARC_DEG );
03617         return which;
03618 }
03619 
03620 void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down)
03621 {
03622         S32 width = getRect().getWidth();
03623         S32 height = getRect().getHeight();
03624 
03625         const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
03626 
03627         LLView* parent_view = getParent();
03628         BOOL moved = FALSE;
03629 
03630         S32 local_x, local_y;
03631         parent_view->screenPointToLocal(x, y, &local_x, &local_y);
03632 
03633         // HACK: casting away const.  Should use setRect or some helper function instead.
03634         const_cast<LLRect&>(getRect()).setCenterAndSize(local_x, local_y, width, height);
03635         arrange();
03636 
03637         // Adjust the pie rectangle to keep it on screen
03638         if (getRect().mLeft < menu_region_rect.mLeft) 
03639         {
03640                 //mShiftHoriz = menu_region_rect.mLeft - getRect().mLeft;
03641                 //getRect().translate( mShiftHoriz, 0 );
03642                 // HACK: casting away const.  Should use setRect or some helper function instead.
03643                 const_cast<LLRect&>(getRect()).translate( menu_region_rect.mLeft - getRect().mLeft, 0 );
03644                 moved = TRUE;
03645         }
03646 
03647         if (getRect().mRight > menu_region_rect.mRight) 
03648         {
03649                 //mShiftHoriz = menu_region_rect.mRight - getRect().mRight;
03650                 //getRect().translate( mShiftHoriz, 0);
03651                 // HACK: casting away const.  Should use setRect or some helper function instead.
03652                 const_cast<LLRect&>(getRect()).translate( menu_region_rect.mRight - getRect().mRight, 0 );
03653                 moved = TRUE;
03654         }
03655 
03656         if (getRect().mBottom < menu_region_rect.mBottom)
03657         {
03658                 //mShiftVert = menu_region_rect.mBottom - getRect().mBottom;
03659                 //getRect().translate( 0, mShiftVert );
03660                 // HACK: casting away const.  Should use setRect or some helper function instead.
03661                 const_cast<LLRect&>(getRect()).translate( 0, menu_region_rect.mBottom - getRect().mBottom );
03662                 moved = TRUE;
03663         }
03664 
03665 
03666         if (getRect().mTop > menu_region_rect.mTop)
03667         {
03668                 //mShiftVert = menu_region_rect.mTop - getRect().mTop;
03669                 //getRect().translate( 0, mShiftVert );
03670                 // HACK: casting away const. Should use setRect or some helper function instead.
03671                 const_cast<LLRect&>(getRect()).translate( 0, menu_region_rect.mTop - getRect().mTop );
03672                 moved = TRUE;
03673         }
03674 
03675         // If we had to relocate the pie menu, put the cursor in the
03676         // center of its rectangle
03677         if (moved)
03678         {
03679                 LLCoordGL center;
03680                 center.mX = (getRect().mLeft + getRect().mRight) / 2;
03681                 center.mY = (getRect().mTop + getRect().mBottom) / 2;
03682 
03683                 LLUI::setCursorPositionLocal(getParent(), center.mX, center.mY);
03684         }
03685 
03686         // *FIX: what happens when mouse buttons reversed?
03687         mRightMouseDown = mouse_down;
03688         mFirstMouseDown = mouse_down;
03689         mUseInfiniteRadius = TRUE;
03690         mHoveredAnyItem = FALSE;
03691 
03692         if (!mFirstMouseDown)
03693         {
03694                 make_ui_sound("UISndPieMenuAppear");
03695         }
03696 
03697         LLView::setVisible(TRUE);
03698 
03699         // we want all mouse events in case user does quick right click again off of pie menu
03700         // rectangle, to support gestural menu traversal
03701         gFocusMgr.setMouseCapture(this);
03702 
03703         if (mouse_down)
03704         {
03705                 mShrinkBorderTimer.stop();
03706         }
03707         else 
03708         {
03709                 mShrinkBorderTimer.start();
03710         }
03711 }
03712 
03713 void LLPieMenu::hide(BOOL item_selected)
03714 {
03715         if (!getVisible()) return;
03716 
03717         if (mHoverItem)
03718         {
03719                 mHoverItem->setHighlight( FALSE );
03720                 mHoverItem = NULL;
03721         }
03722 
03723         make_ui_sound("UISndPieMenuHide");
03724 
03725         mFirstMouseDown = FALSE;
03726         mRightMouseDown = FALSE;
03727         mUseInfiniteRadius = FALSE;
03728         mHoveredAnyItem = FALSE;
03729 
03730         LLView::setVisible(FALSE);
03731 
03732         gFocusMgr.setMouseCapture(NULL);
03733 }
03734 
03738 
03739 static LLRegisterWidget<LLMenuBarGL> r2("menu_bar");
03740 
03741 // Default constructor
03742 LLMenuBarGL::LLMenuBarGL( const LLString& name ) : LLMenuGL ( name, name )
03743 {
03744         mHorizontalLayout = TRUE;
03745         setCanTearOff(FALSE);
03746         mKeepFixedSize = TRUE;
03747         mAltKeyTrigger = FALSE;
03748 }
03749 
03750 // Default destructor
03751 LLMenuBarGL::~LLMenuBarGL()
03752 {
03753         std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
03754         mAccelerators.clear();
03755 }
03756 
03757 // virtual
03758 LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const
03759 {
03760         // Sorty of hacky: reparent items to this and then back at the end of the export
03761         LLView *orig_parent = NULL;
03762         item_list_t::const_iterator item_iter;
03763         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03764         {
03765                 LLMenuItemGL* child = *item_iter;
03766                 LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
03767                 LLMenuGL *menu = branch->getBranch();
03768                 orig_parent = menu->getParent();
03769                 menu->updateParent((LLView *)this);
03770         }
03771 
03772         LLXMLNodePtr node = LLMenuGL::getXML();
03773 
03774         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03775         {
03776                 LLMenuItemGL* child = *item_iter;
03777                 LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
03778                 LLMenuGL *menu = branch->getBranch();
03779                 menu->updateParent(orig_parent);
03780         }
03781 
03782         return node;
03783 }
03784 
03785 LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
03786 {
03787         LLString name("menu");
03788         node->getAttributeString("name", name);
03789 
03790         BOOL opaque = FALSE;
03791         node->getAttributeBOOL("opaque", opaque);
03792 
03793         LLMenuBarGL *menubar = new LLMenuBarGL(name);
03794 
03795         LLHandle<LLFloater> parent_handle;
03796         LLFloater* parent_floater = dynamic_cast<LLFloater*>(parent);
03797         if (parent_floater)
03798         {
03799                 parent_handle = parent_floater->getHandle();
03800         }
03801 
03802         // We need to have the rect early so that it's around when building
03803         // the menu items
03804         LLRect view_rect;
03805         createRect(node, view_rect, parent, menubar->getRequiredRect());
03806         menubar->setRect(view_rect);
03807 
03808         if (node->hasAttribute("drop_shadow"))
03809         {
03810                 BOOL drop_shadow = FALSE;
03811                 node->getAttributeBOOL("drop_shadow", drop_shadow);
03812                 menubar->setDropShadowed(drop_shadow);
03813         }
03814 
03815         menubar->setBackgroundVisible(opaque);
03816         LLColor4 color(0,0,0,0);
03817         if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color))
03818         {
03819                 menubar->setBackgroundColor(color);
03820         }
03821 
03822         LLXMLNodePtr child;
03823         for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
03824         {
03825                 if (child->hasName("menu"))
03826                 {
03827                         LLMenuGL *menu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory);
03828                         // because of lazy initialization, have to disable tear off functionality
03829                         // and then re-enable with proper parent handle
03830                         if (menu->getCanTearOff())
03831                         {
03832                                 menu->setCanTearOff(FALSE);
03833                                 menu->setCanTearOff(TRUE, parent_handle);
03834                         }
03835                         menubar->appendMenu(menu);
03836                         if (LLMenuGL::sMenuContainer != NULL)
03837                         {
03838                                 menu->updateParent(LLMenuGL::sMenuContainer);
03839                         }
03840                         else
03841                         {
03842                                 menu->updateParent(parent);
03843                         }
03844                 }
03845         }
03846 
03847         menubar->initFromXML(node, parent);
03848 
03849         BOOL create_jump_keys = FALSE;
03850         node->getAttributeBOOL("create_jump_keys", create_jump_keys);
03851         if (create_jump_keys)
03852         {
03853                 menubar->createJumpKeys();
03854         }
03855 
03856         return menubar;
03857 }
03858 
03859 BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
03860 {
03861         if (getHighlightedItem() && mask == MASK_NONE)
03862         {
03863                 // unmodified key accelerators are ignored when navigating menu
03864                 // (but are used as jump keys so will still work when appropriate menu is up)
03865                 return FALSE;
03866         }
03867         BOOL result = LLMenuGL::handleAcceleratorKey(key, mask);
03868         if (result && mask & MASK_ALT)
03869         {
03870                 // ALT key used to trigger hotkey, don't use as shortcut to open menu
03871                 mAltKeyTrigger = FALSE;
03872         }
03873 
03874         if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key))
03875         {
03876                 if (getHighlightedItem())
03877                 {
03878                         clearHoverItem();
03879                 }
03880                 else
03881                 {
03882                         highlightNextItem(NULL);
03883                         LLMenuGL::setKeyboardMode(TRUE);
03884                 }
03885                 return TRUE;
03886         }
03887 
03888         return result;
03889 }
03890 
03891 BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
03892 {
03893         if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && LLUI::sConfigGroup->getBOOL("UseAltKeyForMenus"))
03894         {
03895                 mAltKeyTrigger = TRUE;
03896         }
03897         else // if any key other than ALT hit, clear out waiting for Alt key mode
03898         {
03899                 mAltKeyTrigger = FALSE;
03900         }
03901 
03902         // before processing any other key, check to see if ALT key has triggered menu access
03903         checkMenuTrigger();
03904 
03905         return LLMenuGL::handleKeyHere(key, mask);
03906 }
03907 
03908 BOOL LLMenuBarGL::handleJumpKey(KEY key)
03909 {
03910         // perform case-insensitive comparison
03911         key = toupper(key);
03912         navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
03913         if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
03914         {
03915                 // switch to keyboard navigation mode
03916                 LLMenuGL::setKeyboardMode(TRUE);
03917 
03918                 found_it->second->setHighlight(TRUE);
03919                 found_it->second->doIt();
03920         }
03921         return TRUE;
03922 }
03923 
03924 void LLMenuBarGL::draw()
03925 {
03926         LLMenuItemGL* itemp = getHighlightedItem();
03927         // If we are in mouse-control mode and the mouse cursor is not hovering over
03928         // the current highlighted menu item and it isn't open, then remove the highlight.
03929         // This is done via a polling mechanism here, as we don't receive notifications when
03930         // the mouse cursor moves off of us
03931         if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode())
03932         {
03933                 clearHoverItem();
03934         }
03935 
03936         checkMenuTrigger();
03937 
03938         LLMenuGL::draw();
03939 }
03940 
03941 void LLMenuBarGL::checkMenuTrigger()
03942 {
03943         // has the ALT key been pressed and subsequently released?
03944         if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT))
03945         {
03946                 // if alt key was released quickly, treat it as a menu access key
03947                 // otherwise it was probably an Alt-zoom or similar action
03948                 if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= LLUI::sConfigGroup->getF32("MenuAccessKeyTime") ||
03949                         gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2)
03950                 {
03951                         if (getHighlightedItem())
03952                         {
03953                                 clearHoverItem();
03954                         }
03955                         else
03956                         {
03957                                 highlightNextItem(NULL);
03958                                 LLMenuGL::setKeyboardMode(TRUE);
03959                         }
03960                 }
03961                 mAltKeyTrigger = FALSE;
03962         }
03963 }
03964 
03965 BOOL LLMenuBarGL::jumpKeysActive()
03966 {
03967         // require user to be in keyboard navigation mode to activate key triggers
03968         // as menu bars are always visible and it is easy to leave the mouse cursor over them
03969         return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive();
03970 }
03971 
03972 // rearrange the child rects so they fit the shape of the menu bar.
03973 void LLMenuBarGL::arrange( void )
03974 {
03975         U32 pos = 0;
03976         LLRect rect( 0, getRect().getHeight(), 0, 0 );
03977         item_list_t::const_iterator item_iter;
03978         for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
03979         {
03980                 LLMenuItemGL* item = *item_iter;
03981                 if (item->getVisible())
03982                 {
03983                         rect.mLeft = pos;
03984                         pos += item->getNominalWidth();
03985                         rect.mRight = pos;
03986                         item->setRect( rect );
03987                         item->buildDrawLabel();
03988                 }
03989         }
03990         reshape(rect.mRight, rect.getHeight());
03991 }
03992 
03993 
03994 S32 LLMenuBarGL::getRightmostMenuEdge()
03995 {
03996         // Find the last visible menu
03997         item_list_t::reverse_iterator item_iter;
03998         for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
03999         {
04000                 if ((*item_iter)->getVisible())
04001                 {
04002                         break;
04003                 }
04004         }
04005 
04006         if (item_iter == mItems.rend())
04007         {
04008                 return 0;
04009         }
04010         return (*item_iter)->getRect().mRight;
04011 }
04012 
04013 // add a vertical separator to this menu
04014 BOOL LLMenuBarGL::appendSeparator( const LLString &separator_name )
04015 {
04016         LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
04017         return append( separator );
04018 }
04019 
04020 // add a menu - this will create a drop down menu.
04021 BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu )
04022 {
04023         if( menu == this )
04024         {
04025                 llerrs << "** Attempt to attach menu to itself. This is certainly "
04026                            << "a logic error." << llendl;
04027         }
04028 
04029         BOOL success = TRUE;
04030 
04031         LLMenuItemBranchGL* branch = NULL;
04032         branch = new LLMenuItemBranchDownGL( menu->getName(), menu->getLabel(), menu );
04033         success &= branch->addToAcceleratorList(&mAccelerators);
04034         success &= append( branch );
04035         branch->setJumpKey(branch->getJumpKey());
04036         return success;
04037 }
04038 
04039 BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
04040 {
04041         BOOL handled = FALSE;
04042         LLView* active_menu = NULL;
04043 
04044         BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
04045         S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
04046         S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
04047         mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
04048         mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
04049         mLastMouseX = x;
04050         mLastMouseY = y;
04051 
04052         // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
04053         // otherwise let keyboard control it
04054         if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
04055         {
04056                 // find current active menu
04057                 for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
04058                 {
04059                         LLView* viewp = *child_it;
04060                         if (((LLMenuItemGL*)viewp)->isOpen())
04061                         {
04062                                 active_menu = viewp;
04063                         }
04064                 }
04065 
04066                 // check for new active menu
04067                 for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
04068                 {
04069                         LLView* viewp = *child_it;
04070                         S32 local_x = x - viewp->getRect().mLeft;
04071                         S32 local_y = y - viewp->getRect().mBottom;
04072                         if( viewp->getVisible() && 
04073                                 viewp->getEnabled() &&
04074                                 viewp->pointInView(local_x, local_y) && 
04075                                 viewp->handleHover(local_x, local_y, mask))
04076                         {
04077                                 ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
04078                                 handled = TRUE;
04079                                 if (active_menu && active_menu != viewp)
04080                                 {
04081                                         ((LLMenuItemGL*)viewp)->doIt();
04082                                         LLMenuGL::setKeyboardMode(FALSE);
04083                                 }
04084                                 LLMenuGL::setKeyboardMode(FALSE);
04085                         }
04086                 }
04087 
04088                 if (handled)
04089                 {
04090                         // set hover false on inactive menus
04091                         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
04092                         {
04093                                 LLView* viewp = *child_it;
04094                                 S32 local_x = x - viewp->getRect().mLeft;
04095                                 S32 local_y = y - viewp->getRect().mBottom;
04096                                 if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
04097                                 {
04098                                         ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
04099                                 }
04100                         }
04101                 }
04102         }
04103 
04104         getWindow()->setCursor(UI_CURSOR_ARROW);
04105         
04106         return TRUE;
04107 }
04108 
04112 LLMenuHolderGL::LLMenuHolderGL()
04113 :       LLPanel("Menu Holder")
04114 {
04115         setMouseOpaque(FALSE);
04116         sItemActivationTimer.stop();
04117         mCanHide = TRUE;
04118 }
04119 
04120 LLMenuHolderGL::LLMenuHolderGL(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows) 
04121 :       LLPanel(name, rect, FALSE)
04122 {
04123         setMouseOpaque(mouse_opaque);
04124         sItemActivationTimer.stop();
04125         mCanHide = TRUE;
04126 }
04127 
04128 
04129 void LLMenuHolderGL::draw()
04130 {
04131         LLView::draw();
04132         // now draw last selected item as overlay
04133         LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
04134         if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
04135         {
04136                 // make sure toggle items, for example, show the proper state when fading out
04137                 selecteditem->buildDrawLabel();
04138 
04139                 LLRect item_rect;
04140                 selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
04141 
04142                 F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
04143                 F32 alpha = lerp(LLMenuItemGL::getHighlightBGColor().mV[VALPHA], 0.f, interpolant);
04144                 LLColor4 bg_color(LLMenuItemGL::getHighlightBGColor().mV[VRED], 
04145                         LLMenuItemGL::getHighlightBGColor().mV[VGREEN], 
04146                         LLMenuItemGL::getHighlightBGColor().mV[VBLUE], 
04147                         alpha);
04148                 
04149                 LLUI::pushMatrix();
04150                 {
04151                         LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f);
04152                         selecteditem->getMenu()->drawBackground(selecteditem, bg_color);
04153                         selecteditem->draw();
04154                 }
04155                 LLUI::popMatrix();
04156         }
04157 }
04158 
04159 BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
04160 {
04161         BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
04162         if (!handled)
04163         {
04164                 // clicked off of menu, hide them all
04165                 hideMenus();
04166         }
04167         return handled;
04168 }
04169 
04170 BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
04171 {
04172         BOOL handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
04173         if (!handled)
04174         {
04175                 // clicked off of menu, hide them all
04176                 hideMenus();
04177         }
04178         return handled;
04179 }
04180 
04181 void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent)
04182 {
04183         if (width != getRect().getWidth() || height != getRect().getHeight())
04184         {
04185                 hideMenus();
04186         }
04187         LLView::reshape(width, height, called_from_parent);
04188 }
04189 
04190 BOOL LLMenuHolderGL::hasVisibleMenu() const
04191 {
04192         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
04193         {
04194                 LLView* viewp = *child_it;
04195                 if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL)
04196                 {
04197                         return TRUE;
04198                 }
04199         }
04200         return FALSE;
04201 }
04202 
04203 
04204 BOOL LLMenuHolderGL::hideMenus()
04205 {
04206         if (!mCanHide)
04207         {
04208                 return FALSE;
04209         }
04210         BOOL menu_visible = hasVisibleMenu();
04211         if (menu_visible)
04212         {
04213                 LLMenuGL::setKeyboardMode(FALSE);
04214                 // clicked off of menu, hide them all
04215                 for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
04216                 {
04217                         LLView* viewp = *child_it;
04218                         // clicks off of menu do not hide menu bar
04219                         if (dynamic_cast<LLMenuBarGL*>(viewp) == NULL && viewp->getVisible())
04220                         {
04221                                 viewp->setVisible(FALSE);
04222                         }
04223                 }
04224         }
04225         //if (gFocusMgr.childHasKeyboardFocus(this))
04226         //{
04227         //      gFocusMgr.setKeyboardFocus(NULL);
04228         //}
04229 
04230         return menu_visible;
04231 }
04232 
04233 void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
04234 {
04235         sItemLastSelectedHandle = item->getHandle();
04236         sItemActivationTimer.start();
04237 }
04238 
04242 LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : 
04243         LLFloater(menup->getName(), LLRect(0, 100, 100, 0), menup->getLabel(), FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, FALSE)
04244 {
04245         // flag menu as being torn off
04246         menup->setTornOff(TRUE);
04247         // update menu layout as torn off menu (no spillover menus)
04248         menup->arrange();
04249 
04250         LLRect rect;
04251         menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
04252         // make sure this floater is big enough for menu
04253         mTargetHeight = (F32)(rect.getHeight() + LLFLOATER_HEADER_SIZE + 5);
04254         reshape(rect.getWidth(), rect.getHeight());
04255         setRect(rect);
04256 
04257         // attach menu to floater
04258         menup->setFollowsAll();
04259         mOldParent = menup->getParent();
04260         addChild(menup);
04261         menup->setVisible(TRUE);
04262         menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1);
04263         menup->setDropShadowed(FALSE);
04264 
04265         mMenu = menup;
04266 
04267         // highlight first item (tear off item will be disabled)
04268         mMenu->highlightNextItem(NULL);
04269 }
04270 
04271 
04272 void LLTearOffMenu::draw()
04273 {
04274         mMenu->setBackgroundVisible(isBackgroundOpaque());
04275         mMenu->arrange();
04276 
04277         if (getRect().getHeight() != mTargetHeight)
04278         {
04279                 // animate towards target height
04280                 reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f))));
04281         }
04282         else
04283         {
04284                 // when in stasis, remain big enough to hold menu contents
04285                 mTargetHeight = (F32)(mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 4);
04286                 reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 5);
04287         }
04288         LLFloater::draw();
04289 }
04290 
04291 void LLTearOffMenu::onFocusReceived()
04292 {
04293         // if nothing is highlighted, just highlight first item
04294         if (!mMenu->getHighlightedItem())
04295         {
04296                 mMenu->highlightNextItem(NULL);
04297         }
04298 
04299         // parent menu items get highlights so navigation logic keeps working
04300         LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
04301         while(parent_menu_item)
04302         {
04303                 if (parent_menu_item->getMenu()->getVisible())
04304                 {
04305                         parent_menu_item->setHighlight(TRUE);
04306                         parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
04307                 }
04308                 else
04309                 {
04310                         break;
04311                 }
04312         }
04313         LLFloater::onFocusReceived();
04314 }
04315 
04316 void LLTearOffMenu::onFocusLost()
04317 {
04318         // remove highlight from parent item and our own menu
04319         mMenu->clearHoverItem();
04320         LLFloater::onFocusLost();
04321 }
04322 
04323 BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
04324 {
04325         // pass keystrokes down to menu
04326         return mMenu->handleUnicodeChar(uni_char, TRUE);
04327 }
04328 
04329 BOOL LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
04330 {
04331         if (!mMenu->getHighlightedItem())
04332         {
04333                 if (key == KEY_UP)
04334                 {
04335                         mMenu->highlightPrevItem(NULL);         
04336                         return TRUE;
04337                 }
04338                 else if (key == KEY_DOWN)
04339                 {
04340                         mMenu->highlightNextItem(NULL);
04341                         return TRUE;
04342                 }
04343         }
04344         // pass keystrokes down to menu
04345         return mMenu->handleKey(key, mask, TRUE);
04346 }
04347 
04348 void LLTearOffMenu::translate(S32 x, S32 y)
04349 {
04350         if (x != 0 && y != 0)
04351         {
04352                 // hide open sub-menus by clearing current hover item
04353                 mMenu->clearHoverItem();
04354         }
04355         LLFloater::translate(x, y);
04356 }
04357 
04358 //static
04359 LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
04360 {
04361         LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
04362         // keep onscreen
04363         gFloaterView->adjustToFitScreen(tearoffp, FALSE);
04364         tearoffp->open();       /* Flawfinder: ignore */
04365         return tearoffp;
04366 }
04367 
04368 void LLTearOffMenu::onClose(bool app_quitting)
04369 {
04370         removeChild(mMenu);
04371         mOldParent->addChild(mMenu);
04372         mMenu->clearHoverItem();
04373         mMenu->setFollowsNone();
04374         mMenu->setBackgroundVisible(TRUE);
04375         mMenu->setVisible(FALSE);
04376         mMenu->setTornOff(FALSE);
04377         mMenu->setDropShadowed(TRUE);
04378         destroy();
04379 }
04380 

Generated on Fri May 16 08:32:54 2008 for SecondLife by  doxygen 1.5.5