llfloater.cpp

Go to the documentation of this file.
00001 
00032 // Floating "windows" within the GL display, like the inventory floater,
00033 // mini-map floater, etc.
00034 
00035 #include "linden_common.h"
00036 
00037 #include "llfloater.h"
00038 
00039 #include "llfocusmgr.h"
00040 
00041 #include "lluictrlfactory.h"
00042 #include "llbutton.h"
00043 #include "llcheckboxctrl.h"
00044 #include "lldraghandle.h"
00045 #include "llfocusmgr.h"
00046 #include "llresizebar.h"
00047 #include "llresizehandle.h"
00048 #include "llkeyboard.h"
00049 #include "llmenugl.h"   // MENU_BAR_HEIGHT
00050 #include "lltextbox.h"
00051 #include "llresmgr.h"
00052 #include "llui.h"
00053 #include "llwindow.h"
00054 #include "llstl.h"
00055 #include "llcontrol.h"
00056 #include "lltabcontainer.h"
00057 #include "v2math.h"
00058 
00059 extern BOOL gNoRender;
00060 
00061 const S32 MINIMIZED_WIDTH = 160;
00062 const S32 CLOSE_BOX_FROM_TOP = 1;
00063 // use this to control "jumping" behavior when Ctrl-Tabbing
00064 const S32 TABBED_FLOATER_OFFSET = 0;
00065 
00066 LLString        LLFloater::sButtonActiveImageNames[BUTTON_COUNT] = 
00067 {
00068         "UIImgBtnCloseActiveUUID",              //BUTTON_CLOSE
00069         "UIImgBtnRestoreActiveUUID",    //BUTTON_RESTORE
00070         "UIImgBtnMinimizeActiveUUID",   //BUTTON_MINIMIZE
00071         "UIImgBtnTearOffActiveUUID",    //BUTTON_TEAR_OFF
00072         "UIImgBtnCloseActiveUUID",              //BUTTON_EDIT
00073 };
00074 
00075 LLString        LLFloater::sButtonInactiveImageNames[BUTTON_COUNT] = 
00076 {
00077         "UIImgBtnCloseInactiveUUID",    //BUTTON_CLOSE
00078         "UIImgBtnRestoreInactiveUUID",  //BUTTON_RESTORE
00079         "UIImgBtnMinimizeInactiveUUID", //BUTTON_MINIMIZE
00080         "UIImgBtnTearOffInactiveUUID",  //BUTTON_TEAR_OFF
00081         "UIImgBtnCloseInactiveUUID",    //BUTTON_EDIT
00082 };
00083 
00084 LLString        LLFloater::sButtonPressedImageNames[BUTTON_COUNT] = 
00085 {
00086         "UIImgBtnClosePressedUUID",             //BUTTON_CLOSE
00087         "UIImgBtnRestorePressedUUID",   //BUTTON_RESTORE
00088         "UIImgBtnMinimizePressedUUID",  //BUTTON_MINIMIZE
00089         "UIImgBtnTearOffPressedUUID",   //BUTTON_TEAR_OFF
00090         "UIImgBtnClosePressedUUID",             //BUTTON_EDIT
00091 };
00092 
00093 LLString        LLFloater::sButtonNames[BUTTON_COUNT] = 
00094 {
00095         "llfloater_close_btn",  //BUTTON_CLOSE
00096         "llfloater_restore_btn",        //BUTTON_RESTORE
00097         "llfloater_minimize_btn",       //BUTTON_MINIMIZE
00098         "llfloater_tear_off_btn",       //BUTTON_TEAR_OFF
00099         "llfloater_edit_btn",           //BUTTON_EDIT
00100 };
00101 
00102 LLString        LLFloater::sButtonToolTips[BUTTON_COUNT] = 
00103 {
00104 #ifdef LL_DARWIN
00105         "Close (Cmd-W)",        //BUTTON_CLOSE
00106 #else
00107         "Close (Ctrl-W)",       //BUTTON_CLOSE
00108 #endif
00109         "Restore",      //BUTTON_RESTORE
00110         "Minimize",     //BUTTON_MINIMIZE
00111         "Tear Off",     //BUTTON_TEAR_OFF
00112         "Edit",         //BUTTON_EDIT
00113 };
00114 
00115 LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
00116 {
00117         LLFloater::onClickClose,        //BUTTON_CLOSE
00118         LLFloater::onClickMinimize, //BUTTON_RESTORE
00119         LLFloater::onClickMinimize, //BUTTON_MINIMIZE
00120         LLFloater::onClickTearOff,      //BUTTON_TEAR_OFF
00121         LLFloater::onClickEdit, //BUTTON_EDIT
00122 };
00123 
00124 LLMultiFloater* LLFloater::sHostp = NULL;
00125 BOOL                    LLFloater::sEditModeEnabled;
00126 LLFloater::handle_map_t LLFloater::sFloaterMap;
00127 
00128 LLFloaterView* gFloaterView = NULL;
00129 
00130 LLFloater::LLFloater() :
00131         //FIXME: we should initialize *all* member variables here
00132         mResizable(FALSE),
00133         mDragOnLeft(FALSE),
00134         mMinWidth(0),
00135         mMinHeight(0)
00136 {
00137         // automatically take focus when opened
00138         mAutoFocus = TRUE;
00139         for (S32 i = 0; i < BUTTON_COUNT; i++)
00140         {
00141                 mButtonsEnabled[i] = FALSE;
00142                 mButtons[i] = NULL;
00143         }
00144         mDragHandle = NULL;
00145         mHandle.bind(this);
00146 }
00147 
00148 LLFloater::LLFloater(const LLString& name)
00149 :       LLPanel(name), mAutoFocus(TRUE) // automatically take focus when opened
00150 {
00151         for (S32 i = 0; i < BUTTON_COUNT; i++)
00152         {
00153                 mButtonsEnabled[i] = FALSE;
00154                 mButtons[i] = NULL;
00155         }
00156         LLString title; // null string
00157         initFloater(title, FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, TRUE, TRUE); // defaults
00158 }
00159 
00160 
00161 LLFloater::LLFloater(const LLString& name, const LLRect& rect, const LLString& title, 
00162         BOOL resizable, 
00163         S32 min_width, 
00164         S32 min_height,
00165         BOOL drag_on_left,
00166         BOOL minimizable,
00167         BOOL close_btn,
00168         BOOL bordered)
00169 :       LLPanel(name, rect, bordered), mAutoFocus(TRUE) // automatically take focus when opened
00170 {
00171         for (S32 i = 0; i < BUTTON_COUNT; i++)
00172         {
00173                 mButtonsEnabled[i] = FALSE;
00174                 mButtons[i] = NULL;
00175         }
00176         initFloater( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
00177 }
00178 
00179 LLFloater::LLFloater(const LLString& name, const LLString& rect_control, const LLString& title, 
00180         BOOL resizable, 
00181         S32 min_width, 
00182         S32 min_height,
00183         BOOL drag_on_left,
00184         BOOL minimizable,
00185         BOOL close_btn,
00186         BOOL bordered)
00187 :       LLPanel(name, rect_control, bordered), mAutoFocus(TRUE) // automatically take focus when opened
00188 {
00189         for (S32 i = 0; i < BUTTON_COUNT; i++)
00190         {
00191                 mButtonsEnabled[i] = FALSE;
00192                 mButtons[i] = NULL;
00193         }
00194         initFloater( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
00195 }
00196 
00197 
00198 // Note: Floaters constructed from XML call init() twice!
00199 void LLFloater::initFloater(const LLString& title,
00200                                          BOOL resizable, S32 min_width, S32 min_height,
00201                                          BOOL drag_on_left, BOOL minimizable, BOOL close_btn)
00202 {
00203         mHandle.bind(this);
00204 
00205         // Init function can be called more than once, so clear out old data.
00206         for (S32 i = 0; i < BUTTON_COUNT; i++)
00207         {
00208                 mButtonsEnabled[i] = FALSE;
00209                 if (mButtons[i] != NULL)
00210                 {
00211                         removeChild(mButtons[i]);
00212                         delete mButtons[i];
00213                         mButtons[i] = NULL;
00214                 }
00215         }
00216         mButtonScale = 1.f;
00217 
00218         //sjb: Thia is a bit of a hack:
00219         BOOL need_border = hasBorder();
00220         // remove the border since deleteAllChildren() will also delete the border (but not clear mBorder)
00221         removeBorder();
00222         // this will delete mBorder too
00223         deleteAllChildren();
00224         // add the border back if we want it
00225         if (need_border)
00226         {
00227             addBorder();
00228         }
00229 
00230         // chrome floaters don't take focus at all
00231         setFocusRoot(!getIsChrome());
00232 
00233         // Reset cached pointers
00234         mDragHandle = NULL;
00235         for (S32 i = 0; i < 4; i++) 
00236         {
00237                 mResizeBar[i] = NULL;
00238                 mResizeHandle[i] = NULL;
00239         }
00240         mCanTearOff = TRUE;
00241         mEditing = FALSE;
00242 
00243         // Clicks stop here.
00244         setMouseOpaque(TRUE);
00245 
00246         mFirstLook = TRUE;
00247         mForeground = FALSE;
00248         mDragOnLeft = drag_on_left == TRUE;
00249 
00250         // Floaters always draw their background, unlike every other panel.
00251         setBackgroundVisible(TRUE);
00252 
00253         // Floaters start not minimized.  When minimized, they save their
00254         // prior rectangle to be used on restore.
00255         mMinimized = FALSE;
00256         mExpandedRect.set(0,0,0,0);
00257         
00258         S32 close_pad;                  // space to the right of close box
00259         S32 close_box_size;             // For layout purposes, how big is the close box?
00260         if (close_btn)
00261         {
00262                 close_box_size = LLFLOATER_CLOSE_BOX_SIZE;
00263                 close_pad = 0;
00264         }
00265         else
00266         {
00267                 close_box_size = 0;
00268                 close_pad = 0;
00269         }
00270 
00271         S32 minimize_box_size;
00272         S32 minimize_pad;
00273         if (minimizable && !drag_on_left)
00274         {
00275                 minimize_box_size = LLFLOATER_CLOSE_BOX_SIZE;
00276                 minimize_pad = 0;
00277         }
00278         else
00279         {
00280                 minimize_box_size = 0;
00281                 minimize_pad = 0;
00282         }
00283 
00284         // Drag Handle
00285         // Add first so it's in the background.
00286 //      const S32 drag_pad = 2;
00287         if (drag_on_left)
00288         {
00289                 LLRect drag_handle_rect;
00290                 drag_handle_rect.setOriginAndSize(
00291                         0, 0,
00292                         DRAG_HANDLE_WIDTH,
00293                         getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
00294                 mDragHandle = new LLDragHandleLeft("drag", drag_handle_rect, title );
00295         }
00296         else // drag on top
00297         {
00298                 LLRect drag_handle_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 );
00299                 mDragHandle = new LLDragHandleTop( "Drag Handle", drag_handle_rect, title );
00300         }
00301         addChild(mDragHandle);
00302 
00303         // Resize Handle
00304         mResizable = resizable;
00305         mMinWidth = min_width;
00306         mMinHeight = min_height;
00307 
00308         if( mResizable )
00309         {
00310                 // Resize bars (sides)
00311                 const S32 RESIZE_BAR_THICKNESS = 3;
00312                 mResizeBar[LLResizeBar::LEFT] = new LLResizeBar( 
00313                         "resizebar_left",
00314                         this,
00315                         LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0), 
00316                         min_width, S32_MAX, LLResizeBar::LEFT );
00317                 addChild( mResizeBar[0] );
00318 
00319                 mResizeBar[LLResizeBar::TOP] = new LLResizeBar( 
00320                         "resizebar_top",
00321                         this,
00322                         LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS), 
00323                         min_height, S32_MAX, LLResizeBar::TOP );
00324                 addChild( mResizeBar[1] );
00325 
00326                 mResizeBar[LLResizeBar::RIGHT] = new LLResizeBar( 
00327                         "resizebar_right",
00328                         this,
00329                         LLRect( getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0), 
00330                         min_width, S32_MAX, LLResizeBar::RIGHT );
00331                 addChild( mResizeBar[2] );
00332 
00333                 mResizeBar[LLResizeBar::BOTTOM] = new LLResizeBar( 
00334                         "resizebar_bottom",
00335                         this,
00336                         LLRect( 0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0), 
00337                         min_height, S32_MAX, LLResizeBar::BOTTOM );
00338                 addChild( mResizeBar[3] );
00339 
00340 
00341                 // Resize handles (corners)
00342                 mResizeHandle[0] = new LLResizeHandle( 
00343                         "Resize Handle",
00344                         LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0),
00345                         min_width,
00346                         min_height,
00347                         LLResizeHandle::RIGHT_BOTTOM);
00348                 addChild(mResizeHandle[0]);
00349 
00350                 mResizeHandle[1] = new LLResizeHandle( "resize", 
00351                         LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT),
00352                         min_width,
00353                         min_height,
00354                         LLResizeHandle::RIGHT_TOP );
00355                 addChild(mResizeHandle[1]);
00356                 
00357                 mResizeHandle[2] = new LLResizeHandle( "resize", 
00358                         LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
00359                         min_width,
00360                         min_height,
00361                         LLResizeHandle::LEFT_BOTTOM );
00362                 addChild(mResizeHandle[2]);
00363 
00364                 mResizeHandle[3] = new LLResizeHandle( "resize", 
00365                         LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ),
00366                         min_width,
00367                         min_height,
00368                         LLResizeHandle::LEFT_TOP );
00369                 addChild(mResizeHandle[3]);
00370         }
00371 
00372         // Close button.
00373         if (close_btn)
00374         {
00375                 mButtonsEnabled[BUTTON_CLOSE] = TRUE;
00376         }
00377 
00378         // Minimize button only for top draggers
00379         if ( !drag_on_left && minimizable )
00380         {
00381                 mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
00382         }
00383 
00384         // Keep track of whether this window has ever been dragged while it
00385         // was minimized.  If it has, we'll remember its position for the
00386         // next time it's minimized.
00387         mHasBeenDraggedWhileMinimized = FALSE;
00388         mPreviousMinimizedLeft = 0;
00389         mPreviousMinimizedBottom = 0;
00390 
00391         buildButtons();
00392 
00393         // JC - Don't do this here, because many floaters first construct themselves,
00394         // then show themselves.  Put it in setVisibleAndFrontmost.
00395         // make_ui_sound("UISndWindowOpen");
00396 
00397         // RN: floaters are created in the invisible state      
00398         setVisible(FALSE);
00399 
00400         // add self to handle->floater map
00401         sFloaterMap[mHandle] = this;
00402 
00403         if (!getParent())
00404         {
00405                 gFloaterView->addChild(this);
00406         }
00407 }
00408 
00409 // virtual
00410 LLFloater::~LLFloater()
00411 {
00412         control_map_t::iterator itor;
00413         for (itor = mFloaterControls.begin(); itor != mFloaterControls.end(); ++itor)
00414         {
00415                 delete itor->second;
00416         }
00417         mFloaterControls.clear();
00418 
00420         //if (mHostHandle.isDead())
00421         //{
00422         //      LLFloaterView* parent = (LLFloaterView*) getParent();
00423 
00424         //      if( parent )
00425         //      {
00426         //              parent->removeChild( this );
00427         //      }
00428         //}
00429 
00430         // Just in case we might still have focus here, release it.
00431         releaseFocus();
00432 
00433         // This is important so that floaters with persistent rects (i.e., those
00434         // created with rect control rather than an LLRect) are restored in their
00435         // correct, non-minimized positions.
00436         setMinimized( FALSE );
00437 
00438         sFloaterMap.erase(mHandle);
00439 
00440         delete mDragHandle;
00441         for (S32 i = 0; i < 4; i++) 
00442         {
00443                 delete mResizeBar[i];
00444                 delete mResizeHandle[i];
00445         }
00446 }
00447 
00448 
00449 void LLFloater::setVisible( BOOL visible )
00450 {
00451         LLPanel::setVisible(visible);
00452         if( visible && mFirstLook )
00453         {
00454                 mFirstLook = FALSE;
00455         }
00456 
00457         if( !visible )
00458         {
00459                 if( gFocusMgr.childIsTopCtrl( this ) )
00460                 {
00461                         gFocusMgr.setTopCtrl(NULL);
00462                 }
00463 
00464                 if( gFocusMgr.childHasMouseCapture( this ) )
00465                 {
00466                         gFocusMgr.setMouseCapture(NULL);
00467                 }
00468         }
00469 
00470         for(handle_set_iter_t dependent_it = mDependents.begin();
00471                 dependent_it != mDependents.end(); )
00472         {
00473                 LLFloater* floaterp = dependent_it->get();
00474 
00475                 if (floaterp)
00476                 {
00477                         floaterp->setVisible(visible);
00478                 }
00479                 ++dependent_it;
00480         }
00481 }
00482 
00483 void LLFloater::open()  /* Flawfinder: ignore */
00484 {
00485         if (getSoundFlags() != SILENT 
00486         // don't play open sound for hosted (tabbed) windows
00487                 && !getHost() 
00488                 && !getFloaterHost()
00489                 && (!getVisible() || isMinimized()))
00490         {
00491                 make_ui_sound("UISndWindowOpen");
00492         }
00493 
00494         //RN: for now, we don't allow rehosting from one multifloater to another
00495         // just need to fix the bugs
00496         if (getFloaterHost() != NULL && getHost() == NULL)
00497         {
00498                 // needs a host
00499                 // only select tabs if window they are hosted in is visible
00500                 getFloaterHost()->addFloater(this, getFloaterHost()->getVisible());
00501         }
00502         else if (getHost() != NULL)
00503         {
00504                 // already hosted
00505                 getHost()->showFloater(this);
00506         }
00507         else
00508         {
00509                 setMinimized(FALSE);
00510                 setVisibleAndFrontmost(mAutoFocus);
00511         }
00512 
00513         onOpen();
00514 }
00515 
00516 void LLFloater::close(bool app_quitting)
00517 {
00518         // Always unminimize before trying to close.
00519         // Most of the time the user will never see this state.
00520         setMinimized(FALSE);
00521 
00522         if (canClose())
00523         {
00524                 if (getHost())
00525                 {
00526                         ((LLMultiFloater*)getHost())->removeFloater(this);
00527                 }
00528 
00529                 if (getSoundFlags() != SILENT
00530                         && getVisible()
00531                         && !getHost()
00532                         && !app_quitting)
00533                 {
00534                         make_ui_sound("UISndWindowClose");
00535                 }
00536 
00537                 // now close dependent floater
00538                 for(handle_set_iter_t dependent_it = mDependents.begin();
00539                         dependent_it != mDependents.end(); )
00540                 {
00541                         
00542                         LLFloater* floaterp = dependent_it->get();
00543                         if (floaterp)
00544                         {
00545                                 ++dependent_it;
00546                                 floaterp->close();
00547                         }
00548                         else
00549                         {
00550                                 mDependents.erase(dependent_it++);
00551                         }
00552                 }
00553                 
00554                 cleanupHandles();
00555                 gFocusMgr.clearLastFocusForGroup(this);
00556 
00557                 if (hasFocus())
00558                 {
00559                         // Do this early, so UI controls will commit before the
00560                         // window is taken down.
00561                         releaseFocus();
00562 
00563                         // give focus to dependee floater if it exists, and we had focus first
00564                         if (isDependent())
00565                         {
00566                                 LLFloater* dependee = mDependeeHandle.get();
00567                                 if (dependee && !dependee->isDead())
00568                                 {
00569                                         dependee->setFocus(TRUE);
00570                                 }
00571                         }
00572                 }
00573 
00574                 // Let floater do cleanup.
00575                 onClose(app_quitting);
00576         }
00577 }
00578 
00579 /*virtual*/
00580 void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
00581 {
00582         LLPanel::reshape(width, height, called_from_parent);
00583 }
00584 
00585 void LLFloater::releaseFocus()
00586 {
00587         if( gFocusMgr.childIsTopCtrl( this ) )
00588         {
00589                 gFocusMgr.setTopCtrl(NULL);
00590         }
00591 
00592         if( gFocusMgr.childHasKeyboardFocus( this ) )
00593         {
00594                 gFocusMgr.setKeyboardFocus(NULL);
00595         }
00596 
00597         if( gFocusMgr.childHasMouseCapture( this ) )
00598         {
00599                 gFocusMgr.setMouseCapture(NULL);
00600         }
00601 }
00602 
00603 
00604 void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
00605 {
00606         mMinWidth = min_width;
00607         mMinHeight = min_height;
00608 
00609         for( S32 i = 0; i < 4; i++ )
00610         {
00611                 if( mResizeBar[i] )
00612                 {
00613                         if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT)
00614                         {
00615                                 mResizeBar[i]->setResizeLimits( min_width, S32_MAX );
00616                         }
00617                         else
00618                         {
00619                                 mResizeBar[i]->setResizeLimits( min_height, S32_MAX );
00620                         }
00621                 }
00622                 if( mResizeHandle[i] )
00623                 {
00624                         mResizeHandle[i]->setResizeLimits( min_width, min_height );
00625                 }
00626         }
00627 }
00628 
00629 
00630 void LLFloater::center()
00631 {
00632         if(getHost())
00633         {
00634                 // hosted floaters can't move
00635                 return;
00636         }
00637         centerWithin(gFloaterView->getRect());
00638 }
00639 
00640 void LLFloater::applyRectControl()
00641 {
00642         if (!getRectControl().empty())
00643         {
00644                 const LLRect& rect = LLUI::sConfigGroup->getRect(getRectControl());
00645                 translate( rect.mLeft - getRect().mLeft, rect.mBottom - getRect().mBottom);
00646                 if (mResizable)
00647                 {
00648                         reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
00649                 }
00650         }
00651 }
00652 
00653 void LLFloater::setTitle( const LLString& title )
00654 {
00655         if (gNoRender)
00656         {
00657                 return;
00658         }
00659         if (mDragHandle)
00660                 mDragHandle->setTitle( title );
00661 }
00662 
00663 const LLString& LLFloater::getTitle() const
00664 {
00665         return mDragHandle ? mDragHandle->getTitle() : LLString::null;
00666 }
00667 
00668 void LLFloater::setShortTitle( const LLString& short_title )
00669 {
00670         mShortTitle = short_title;
00671 }
00672 
00673 LLString LLFloater::getShortTitle()
00674 {
00675         if (mShortTitle.empty())
00676         {
00677                 return mDragHandle ? mDragHandle->getTitle() : LLString::null;
00678         }
00679         else
00680         {
00681                 return mShortTitle;
00682         }
00683 }
00684 
00685 
00686 
00687 BOOL LLFloater::canSnapTo(const LLView* other_view)
00688 {
00689         if (NULL == other_view)
00690         {
00691                 llwarns << "other_view is NULL" << llendl;
00692                 return FALSE;
00693         }
00694 
00695         if (other_view != getParent())
00696         {
00697                 LLFloater* other_floaterp = (LLFloater*)other_view;
00698                 
00699                 if (other_floaterp->getSnapTarget() == getHandle() && mDependents.find(other_floaterp->getHandle()) != mDependents.end())
00700                 {
00701                         // this is a dependent that is already snapped to us, so don't snap back to it
00702                         return FALSE;
00703                 }
00704         }
00705 
00706         return LLPanel::canSnapTo(other_view);
00707 }
00708 
00709 void LLFloater::snappedTo(const LLView* snap_view)
00710 {
00711         if (!snap_view || snap_view == getParent())
00712         {
00713                 clearSnapTarget();
00714         }
00715         else
00716         {
00717                 //RN: assume it's a floater as it must be a sibling to our parent floater
00718                 LLFloater* floaterp = (LLFloater*)snap_view;
00719                 
00720                 setSnapTarget(floaterp->getHandle());
00721         }
00722 }
00723 
00724 void LLFloater::userSetShape(const LLRect& new_rect)
00725 {
00726         const LLRect& old_rect = getRect();
00727         LLView::userSetShape(new_rect);
00728 
00729         // if not minimized, adjust all snapped dependents to new shape
00730         if (!isMinimized())
00731         {
00732                 // gather all snapped dependents
00733                 for(handle_set_iter_t dependent_it = mDependents.begin();
00734                         dependent_it != mDependents.end(); ++dependent_it)
00735                 {
00736                         LLFloater* floaterp = dependent_it->get();
00737                         // is a dependent snapped to us?
00738                         if (floaterp && floaterp->getSnapTarget() == getHandle())
00739                         {
00740                                 S32 delta_x = 0;
00741                                 S32 delta_y = 0;
00742                                 // check to see if it snapped to right or top, and move if dependee floater is resizing
00743                                 LLRect dependent_rect = floaterp->getRect();
00744                                 if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right?
00745                                         dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right
00746                                 {
00747                                         // was snapped directly onto right side or aligned with it
00748                                         delta_x += new_rect.getWidth() - old_rect.getWidth();
00749                                 }
00750                                 if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() ||
00751                                         dependent_rect.mTop == getRect().mBottom + old_rect.getHeight())
00752                                 {
00753                                         // was snapped directly onto top side or aligned with it
00754                                         delta_y += new_rect.getHeight() - old_rect.getHeight();
00755                                 }
00756 
00757                                 // take translation of dependee floater into account as well
00758                                 delta_x += new_rect.mLeft - old_rect.mLeft;
00759                                 delta_y += new_rect.mBottom - old_rect.mBottom;
00760 
00761                                 dependent_rect.translate(delta_x, delta_y);
00762                                 floaterp->userSetShape(dependent_rect);
00763                         }
00764                 }
00765         }
00766         else
00767         {
00768                 // If minimized, and origin has changed, set
00769                 // mHasBeenDraggedWhileMinimized to TRUE
00770                 if ((new_rect.mLeft != old_rect.mLeft) ||
00771                         (new_rect.mBottom != old_rect.mBottom))
00772                 {
00773                         mHasBeenDraggedWhileMinimized = TRUE;
00774                 }
00775         }
00776 }
00777 
00778 void LLFloater::setMinimized(BOOL minimize)
00779 {
00780         if (minimize == mMinimized) return;
00781 
00782         if (minimize)
00783         {
00784                 mExpandedRect = getRect();
00785 
00786                 // If the floater has been dragged while minimized in the
00787                 // past, then locate it at its previous minimized location.
00788                 // Otherwise, ask the view for a minimize position.
00789                 if (mHasBeenDraggedWhileMinimized)
00790                 {
00791                         setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom);
00792                 }
00793                 else
00794                 {
00795                         S32 left, bottom;
00796                         gFloaterView->getMinimizePosition(&left, &bottom);
00797                         setOrigin( left, bottom );
00798                 }
00799 
00800                 if (mButtonsEnabled[BUTTON_MINIMIZE])
00801                 {
00802                         mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
00803                         mButtonsEnabled[BUTTON_RESTORE] = TRUE;
00804                 }
00805 
00806                 if (mDragHandle)
00807                 {
00808                         mDragHandle->setVisible(TRUE);
00809                 }
00810                 setBorderVisible(TRUE);
00811 
00812                 for(handle_set_iter_t dependent_it = mDependents.begin();
00813                         dependent_it != mDependents.end();
00814                         ++dependent_it)
00815                 {
00816                         LLFloater* floaterp = dependent_it->get();
00817                         if (floaterp)
00818                         {
00819                                 if (floaterp->isMinimizeable())
00820                                 {
00821                                         floaterp->setMinimized(TRUE);
00822                                 }
00823                                 else if (!floaterp->isMinimized())
00824                                 {
00825                                         floaterp->setVisible(FALSE);
00826                                 }
00827                         }
00828                 }
00829 
00830                 // Lose keyboard focus when minimized
00831                 releaseFocus();
00832 
00833                 for (S32 i = 0; i < 4; i++)
00834                 {
00835                         if (mResizeBar[i] != NULL)
00836                         {
00837                                 mResizeBar[i]->setEnabled(FALSE);
00838                         }
00839                         if (mResizeHandle[i] != NULL)
00840                         {
00841                                 mResizeHandle[i]->setEnabled(FALSE);
00842                         }
00843                 }
00844 
00845                 mMinimized = TRUE;
00846 
00847                 // Reshape *after* setting mMinimized
00848                 reshape( MINIMIZED_WIDTH, LLFLOATER_HEADER_SIZE, TRUE);
00849         }
00850         else
00851         {
00852                 // If this window has been dragged while minimized (at any time),
00853                 // remember its position for the next time it's minimized.
00854                 if (mHasBeenDraggedWhileMinimized)
00855                 {
00856                         const LLRect& currentRect = getRect();
00857                         mPreviousMinimizedLeft = currentRect.mLeft;
00858                         mPreviousMinimizedBottom = currentRect.mBottom;
00859                 }
00860 
00861                 setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
00862 
00863                 if (mButtonsEnabled[BUTTON_RESTORE])
00864                 {
00865                         mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
00866                         mButtonsEnabled[BUTTON_RESTORE] = FALSE;
00867                 }
00868 
00869                 // show dependent floater
00870                 for(handle_set_iter_t dependent_it = mDependents.begin();
00871                         dependent_it != mDependents.end();
00872                         ++dependent_it)
00873                 {
00874                         LLFloater* floaterp = dependent_it->get();
00875                         if (floaterp)
00876                         {
00877                                 floaterp->setMinimized(FALSE);
00878                                 floaterp->setVisible(TRUE);
00879                         }
00880                 }
00881 
00882                 for (S32 i = 0; i < 4; i++)
00883                 {
00884                         if (mResizeBar[i] != NULL)
00885                         {
00886                                 mResizeBar[i]->setEnabled(isResizable());
00887                         }
00888                         if (mResizeHandle[i] != NULL)
00889                         {
00890                                 mResizeHandle[i]->setEnabled(isResizable());
00891                         }
00892                 }
00893 
00894                 mMinimized = FALSE;
00895 
00896                 // Reshape *after* setting mMinimized
00897                 reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE );
00898         }
00899         make_ui_sound("UISndWindowClose");
00900         updateButtons();
00901 }
00902 
00903 void LLFloater::setFocus( BOOL b )
00904 {
00905         if (b && getIsChrome())
00906         {
00907                 return;
00908         }
00909         LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
00910         // a descendent already has focus
00911         BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this);
00912 
00913         // give focus to first valid descendent
00914         LLPanel::setFocus(b);
00915 
00916         if (b)
00917         {
00918                 // only push focused floaters to front of stack if not in midst of ctrl-tab cycle
00919                 if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode())
00920                 {
00921                         if (!isFrontmost())
00922                         {
00923                                 setFrontmost();
00924                         }
00925                 }
00926 
00927                 // when getting focus, delegate to last descendent which had focus
00928                 if (last_focus && !child_had_focus && 
00929                         last_focus->isInEnabledChain() &&
00930                         last_focus->isInVisibleChain())
00931                 {
00932                         // *FIX: should handle case where focus doesn't stick
00933                         last_focus->setFocus(TRUE);
00934                 }
00935         }
00936 }
00937 
00938 // virtual
00939 void LLFloater::setIsChrome(BOOL is_chrome)
00940 {
00941         // chrome floaters don't take focus at all
00942         if (is_chrome)
00943         {
00944                 // remove focus if we're changing to chrome
00945                 setFocus(FALSE);
00946                 // can't Ctrl-Tab to "chrome" floaters
00947                 setFocusRoot(FALSE);
00948         }
00949         
00950         // no titles displayed on "chrome" floaters
00951         if (mDragHandle)
00952                 mDragHandle->setTitleVisible(!is_chrome);
00953         
00954         LLPanel::setIsChrome(is_chrome);
00955 }
00956 
00957 void LLFloater::setTitleVisible(bool visible)
00958 {
00959         if (mDragHandle)
00960                 mDragHandle->setTitleVisible(visible);
00961 }
00962 
00963 // Change the draw style to account for the foreground state.
00964 void LLFloater::setForeground(BOOL front)
00965 {
00966         if (front != mForeground)
00967         {
00968                 mForeground = front;
00969                 if (mDragHandle)
00970                         mDragHandle->setForeground( front );
00971 
00972                 if (!front)
00973                 {
00974                         releaseFocus();
00975                 }
00976 
00977                 setBackgroundOpaque( front ); 
00978         }
00979 }
00980 
00981 void LLFloater::cleanupHandles()
00982 {
00983         // remove handles to non-existent dependents
00984         for(handle_set_iter_t dependent_it = mDependents.begin();
00985                 dependent_it != mDependents.end(); )
00986         {
00987                 LLFloater* floaterp = dependent_it->get();
00988                 if (!floaterp)
00989                 {
00990                         mDependents.erase(dependent_it++);
00991                 }
00992                 else
00993                 {
00994                         ++dependent_it;
00995                 }
00996         }
00997 }
00998 
00999 void LLFloater::setHost(LLMultiFloater* host)
01000 {
01001         if (mHostHandle.isDead() && host)
01002         {
01003                 // make buttons smaller for hosted windows to differentiate from parent
01004                 mButtonScale = 0.9f;
01005 
01006                 // add tear off button
01007                 if (mCanTearOff)
01008                 {
01009                         mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE;
01010                 }
01011         }
01012         else if (!mHostHandle.isDead() && !host)
01013         {
01014                 mButtonScale = 1.f;
01015                 //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
01016         }
01017         updateButtons();
01018         if (host)
01019         {
01020                 mHostHandle = host->getHandle();
01021                 mLastHostHandle = host->getHandle();
01022         }
01023         else
01024         {
01025                 mHostHandle.markDead();
01026         }
01027 }
01028 
01029 void LLFloater::moveResizeHandlesToFront()
01030 {
01031         for( S32 i = 0; i < 4; i++ )
01032         {
01033                 if( mResizeBar[i] )
01034                 {
01035                         sendChildToFront(mResizeBar[i]);
01036                 }
01037         }
01038 
01039         for( S32 i = 0; i < 4; i++ )
01040         {
01041                 if( mResizeHandle[i] )
01042                 {
01043                         sendChildToFront(mResizeHandle[i]);
01044                 }
01045         }
01046 }
01047 
01048 BOOL LLFloater::isFrontmost()
01049 {
01050         return gFloaterView && gFloaterView->getFrontmost() == this && getVisible();
01051 }
01052 
01053 void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
01054 {
01055         mDependents.insert(floaterp->getHandle());
01056         floaterp->mDependeeHandle = getHandle();
01057 
01058         if (reposition)
01059         {
01060                 floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
01061                 floaterp->setSnapTarget(getHandle());
01062         }
01063         gFloaterView->adjustToFitScreen(floaterp, FALSE);
01064         if (floaterp->isFrontmost())
01065         {
01066                 // make sure to bring self and sibling floaters to front
01067                 gFloaterView->bringToFront(floaterp);
01068         }
01069 }
01070 
01071 void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition)
01072 {
01073         LLFloater* dependent_floaterp = dependent.get();
01074         if(dependent_floaterp)
01075         {
01076                 addDependentFloater(dependent_floaterp, reposition);
01077         }
01078 }
01079 
01080 void LLFloater::removeDependentFloater(LLFloater* floaterp)
01081 {
01082         mDependents.erase(floaterp->getHandle());
01083         floaterp->mDependeeHandle = LLHandle<LLFloater>();
01084 }
01085 
01086 // virtual
01087 BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
01088 {
01089         if( mMinimized )
01090         {
01091                 // Offer the click to the close button.
01092                 if( mButtonsEnabled[BUTTON_CLOSE] )
01093                 {
01094                         S32 local_x = x - mButtons[BUTTON_CLOSE]->getRect().mLeft;
01095                         S32 local_y = y - mButtons[BUTTON_CLOSE]->getRect().mBottom;
01096 
01097                         if (mButtons[BUTTON_CLOSE]->pointInView(local_x, local_y)
01098                                 && mButtons[BUTTON_CLOSE]->handleMouseDown(local_x, local_y, mask))
01099                         {
01100                                 // close button handled it, return
01101                                 return TRUE;
01102                         }
01103                 }
01104 
01105                 // Offer the click to the restore button.
01106                 if( mButtonsEnabled[BUTTON_RESTORE] )
01107                 {
01108                         S32 local_x = x - mButtons[BUTTON_RESTORE]->getRect().mLeft;
01109                         S32 local_y = y - mButtons[BUTTON_RESTORE]->getRect().mBottom;
01110 
01111                         if (mButtons[BUTTON_RESTORE]->pointInView(local_x, local_y)
01112                                 && mButtons[BUTTON_RESTORE]->handleMouseDown(local_x, local_y, mask))
01113                         {
01114                                 // restore button handled it, return
01115                                 return TRUE;
01116                         }
01117                 }
01118 
01119                 // Otherwise pass to drag handle for movement
01120                 return mDragHandle->handleMouseDown(x, y, mask);
01121         }
01122         else
01123         {
01124                 bringToFront( x, y );
01125                 return LLPanel::handleMouseDown( x, y, mask );
01126         }
01127 }
01128 
01129 // virtual
01130 BOOL LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
01131 {
01132         BOOL was_minimized = mMinimized;
01133         bringToFront( x, y );
01134         return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
01135 }
01136 
01137 
01138 // virtual
01139 BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
01140 {
01141         BOOL was_minimized = mMinimized;
01142         setMinimized(FALSE);
01143         return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
01144 }
01145 
01146 void LLFloater::bringToFront( S32 x, S32 y )
01147 {
01148         if (getVisible() && pointInView(x, y))
01149         {
01150                 LLMultiFloater* hostp = getHost();
01151                 if (hostp)
01152                 {
01153                         hostp->showFloater(this);
01154                 }
01155                 else
01156                 {
01157                         LLFloaterView* parent = (LLFloaterView*) getParent();
01158                         if (parent)
01159                         {
01160                                 parent->bringToFront( this );
01161                         }
01162                 }
01163         }
01164 }
01165 
01166 
01167 // virtual
01168 void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
01169 {
01170         setVisible(TRUE);
01171         setFrontmost(take_focus);
01172 }
01173 
01174 void LLFloater::setFrontmost(BOOL take_focus)
01175 {
01176         LLMultiFloater* hostp = getHost();
01177         if (hostp)
01178         {
01179                 // this will bring the host floater to the front and select
01180                 // the appropriate panel
01181                 hostp->showFloater(this);
01182         }
01183         else
01184         {
01185                 // there are more than one floater view
01186                 // so we need to query our parent directly
01187                 ((LLFloaterView*)getParent())->bringToFront(this, take_focus);
01188         }
01189 }
01190 
01191 //static
01192 void LLFloater::setEditModeEnabled(BOOL enable)
01193 {
01194         if (enable != sEditModeEnabled)
01195         {
01196                 S32 count = 0;
01197                 for(handle_map_iter_t iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
01198                 {
01199                         LLFloater* floater = iter->second;
01200                         if (!floater->isDead())
01201                         {
01202                                 iter->second->mButtonsEnabled[BUTTON_EDIT] = enable;
01203                                 iter->second->updateButtons();
01204                         }
01205                         count++;
01206                 }
01207         }
01208 
01209         sEditModeEnabled = enable;
01210 }
01211 
01212 
01213 // static
01214 void LLFloater::onClickMinimize(void *userdata)
01215 {
01216         LLFloater* self = (LLFloater*) userdata;
01217         if (!self) return;
01218 
01219         self->setMinimized( !self->isMinimized() );
01220 }
01221 
01222 void LLFloater::onClickTearOff(void *userdata)
01223 {
01224         LLFloater* self = (LLFloater*) userdata;
01225         if (!self) return;
01226 
01227         LLMultiFloater* host_floater = self->getHost();
01228         if (host_floater) //Tear off
01229         {
01230                 LLRect new_rect;
01231                 host_floater->removeFloater(self);
01232                 // reparent to floater view
01233                 gFloaterView->addChild(self);
01234 
01235                 self->open();   /* Flawfinder: ignore */
01236                 
01237                 // only force position for floaters that don't have that data saved
01238                 if (self->getRectControl().empty())
01239                 {
01240                         new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - LLFLOATER_HEADER_SIZE - 5, self->getRect().getWidth(), self->getRect().getHeight());
01241                         self->setRect(new_rect);
01242                 }
01243                 gFloaterView->adjustToFitScreen(self, FALSE);
01244                 // give focus to new window to keep continuity for the user
01245                 self->setFocus(TRUE);
01246         }
01247         else  //Attach to parent.
01248         {
01249                 LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
01250                 if (new_host)
01251                 {
01252                         new_host->showFloater(self);
01253                         // make sure host is visible
01254                         new_host->open();
01255                 }
01256         }
01257 }
01258 
01259 // static
01260 void LLFloater::onClickEdit(void *userdata)
01261 {
01262         LLFloater* self = (LLFloater*) userdata;
01263         if (!self) return;
01264 
01265         self->mEditing = self->mEditing ? FALSE : TRUE;
01266 }
01267 
01268 // static
01269 void LLFloater::closeFocusedFloater()
01270 {
01271         LLFloater* focused_floater = NULL;
01272 
01273         handle_map_iter_t iter;
01274         for(iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
01275         {
01276                 focused_floater = iter->second;
01277                 if (focused_floater->hasFocus())
01278                 {
01279                         break;
01280                 }
01281         }
01282 
01283         if (iter == sFloaterMap.end())
01284         {
01285                 // nothing found, return
01286                 return;
01287         }
01288 
01289         focused_floater->close();
01290 
01291         // if nothing took focus after closing focused floater
01292         // give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
01293         if (gFocusMgr.getKeyboardFocus() == NULL)
01294         {
01295                 // HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window
01296                 // which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view
01297                 gFloaterView->focusFrontFloater();
01298         }
01299 }
01300 
01301 
01302 // static
01303 void LLFloater::onClickClose( void* userdata )
01304 {
01305         LLFloater* self = (LLFloater*) userdata;
01306         if (!self) return;
01307 
01308         self->close();
01309 }
01310 
01311 
01312 // virtual
01313 void LLFloater::draw()
01314 {
01315         // draw background
01316         if( isBackgroundVisible() )
01317         {
01318                 S32 left = LLPANEL_BORDER_WIDTH;
01319                 S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH;
01320                 S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH;
01321                 S32 bottom = LLPANEL_BORDER_WIDTH;
01322 
01323                 LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow");
01324                 F32 shadow_offset = (F32)LLUI::sConfigGroup->getS32("DropShadowFloater");
01325                 if (!isBackgroundOpaque())
01326                 {
01327                         shadow_offset *= 0.2f;
01328                         shadow_color.mV[VALPHA] *= 0.5f;
01329                 }
01330                 gl_drop_shadow(left, top, right, bottom, 
01331                         shadow_color, 
01332                         llround(shadow_offset));
01333 
01334                 // No transparent windows in simple UI
01335                 if (isBackgroundOpaque())
01336                 {
01337                         gl_rect_2d( left, top, right, bottom, getBackgroundColor() );
01338                 }
01339                 else
01340                 {
01341                         gl_rect_2d( left, top, right, bottom, getTransparentColor() );
01342                 }
01343 
01344                 if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getTitle().empty())
01345                 {
01346                         // draw highlight on title bar to indicate focus.  RDW
01347                         const LLFontGL* font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF );
01348                         LLRect r = getRect();
01349                         gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, 
01350                                 LLUI::sColorsGroup->getColor("TitleBarFocusColor"), 0, TRUE);
01351                 }
01352         }
01353 
01354         LLPanel::updateDefaultBtn();
01355 
01356         if( getDefaultButton() )
01357         {
01358                 if (hasFocus() && getDefaultButton()->getEnabled())
01359                 {
01360                         LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
01361                         // is this button a direct descendent and not a nested widget (e.g. checkbox)?
01362                         BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && focus_ctrl->getParent() == this;
01363                         // only enable default button when current focus is not a button
01364                         getDefaultButton()->setBorderEnabled(!focus_is_child_button);
01365                 }
01366                 else
01367                 {
01368                         getDefaultButton()->setBorderEnabled(FALSE);
01369                 }
01370         }
01371         if (isMinimized())
01372         {
01373                 for (S32 i = 0; i < BUTTON_COUNT; i++)
01374                 {
01375                         drawChild(mButtons[i]);
01376                 }
01377                 drawChild(mDragHandle);
01378         }
01379         else
01380         {
01381                 // draw children
01382                 LLView* focused_child = gFocusMgr.getKeyboardFocus();
01383                 BOOL focused_child_visible = FALSE;
01384                 if (focused_child && focused_child->getParent() == this)
01385                 {
01386                         focused_child_visible = focused_child->getVisible();
01387                         focused_child->setVisible(FALSE);
01388                 }
01389 
01390                 // don't call LLPanel::draw() since we've implemented custom background rendering
01391                 LLView::draw();
01392 
01393                 if (focused_child_visible)
01394                 {
01395                         focused_child->setVisible(TRUE);
01396                 }
01397                 drawChild(focused_child);
01398         }
01399 
01400         if( isBackgroundVisible() )
01401         {
01402                 // add in a border to improve spacialized visual aclarity ;)
01403                 // use lines instead of gl_rect_2d so we can round the edges as per james' recommendation
01404                 LLUI::setLineWidth(1.5f);
01405                 LLColor4 outlineColor = gFocusMgr.childHasKeyboardFocus(this) ? LLUI::sColorsGroup->getColor("FloaterFocusBorderColor") : LLUI::sColorsGroup->getColor("FloaterUnfocusBorderColor");
01406                 gl_rect_2d_offset_local(0, getRect().getHeight() + 1, getRect().getWidth() + 1, 0, outlineColor, -LLPANEL_BORDER_WIDTH, FALSE);
01407                 LLUI::setLineWidth(1.f);
01408         }
01409 
01410         // update tearoff button for torn off floaters
01411         // when last host goes away
01412         if (mCanTearOff && !getHost())
01413         {
01414                 LLFloater* old_host = mLastHostHandle.get();
01415                 if (!old_host)
01416                 {
01417                         setCanTearOff(FALSE);
01418                 }
01419         }
01420 }
01421 
01422 void    LLFloater::setCanMinimize(BOOL can_minimize)
01423 {
01424         // removing minimize/restore button programmatically,
01425         // go ahead and uniminimize floater
01426         if (!can_minimize)
01427         {
01428                 setMinimized(FALSE);
01429         }
01430 
01431         if (can_minimize)
01432         {
01433                 if (isMinimized())
01434                 {
01435                         mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
01436                         mButtonsEnabled[BUTTON_RESTORE] = TRUE;
01437                 }
01438                 else
01439                 {
01440                         mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
01441                         mButtonsEnabled[BUTTON_RESTORE] = FALSE;
01442                 }
01443         }
01444         else
01445         {
01446                 mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
01447                 mButtonsEnabled[BUTTON_RESTORE] = FALSE;
01448         }
01449 
01450         updateButtons();
01451 }
01452 
01453 void    LLFloater::setCanClose(BOOL can_close)
01454 {
01455         mButtonsEnabled[BUTTON_CLOSE] = can_close;
01456 
01457         updateButtons();
01458 }
01459 
01460 void    LLFloater::setCanTearOff(BOOL can_tear_off)
01461 {
01462         mCanTearOff = can_tear_off;
01463         mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
01464 
01465         updateButtons();
01466 }
01467 
01468 
01469 void    LLFloater::setCanResize(BOOL can_resize)
01470 {
01471         if (mResizable && !can_resize)
01472         {
01473                 for (S32 i = 0; i < 4; i++) 
01474                 {
01475                         removeChild(mResizeBar[i], TRUE);
01476                         mResizeBar[i] = NULL; 
01477 
01478                         removeChild(mResizeHandle[i], TRUE);
01479                         mResizeHandle[i] = NULL;
01480                 }
01481         }
01482         else if (!mResizable && can_resize)
01483         {
01484                 // Resize bars (sides)
01485                 const S32 RESIZE_BAR_THICKNESS = 3;
01486                 mResizeBar[0] = new LLResizeBar( 
01487                         "resizebar_left",
01488                         this,
01489                         LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0), 
01490                         mMinWidth, S32_MAX, LLResizeBar::LEFT );
01491                 addChild( mResizeBar[0] );
01492 
01493                 mResizeBar[1] = new LLResizeBar( 
01494                         "resizebar_top",
01495                         this,
01496                         LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS), 
01497                         mMinHeight, S32_MAX, LLResizeBar::TOP );
01498                 addChild( mResizeBar[1] );
01499 
01500                 mResizeBar[2] = new LLResizeBar( 
01501                         "resizebar_right",
01502                         this,
01503                         LLRect( getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0), 
01504                         mMinWidth, S32_MAX, LLResizeBar::RIGHT );
01505                 addChild( mResizeBar[2] );
01506 
01507                 mResizeBar[3] = new LLResizeBar( 
01508                         "resizebar_bottom",
01509                         this,
01510                         LLRect( 0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0), 
01511                         mMinHeight, S32_MAX, LLResizeBar::BOTTOM );
01512                 addChild( mResizeBar[3] );
01513 
01514 
01515                 // Resize handles (corners)
01516                 mResizeHandle[0] = new LLResizeHandle( 
01517                         "Resize Handle",
01518                         LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0),
01519                         mMinWidth,
01520                         mMinHeight,
01521                         LLResizeHandle::RIGHT_BOTTOM);
01522                 addChild(mResizeHandle[0]);
01523 
01524                 mResizeHandle[1] = new LLResizeHandle( "resize", 
01525                         LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT),
01526                         mMinWidth,
01527                         mMinHeight,
01528                         LLResizeHandle::RIGHT_TOP );
01529                 addChild(mResizeHandle[1]);
01530                 
01531                 mResizeHandle[2] = new LLResizeHandle( "resize", 
01532                         LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
01533                         mMinWidth,
01534                         mMinHeight,
01535                         LLResizeHandle::LEFT_BOTTOM );
01536                 addChild(mResizeHandle[2]);
01537 
01538                 mResizeHandle[3] = new LLResizeHandle( "resize", 
01539                         LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ),
01540                         mMinWidth,
01541                         mMinHeight,
01542                         LLResizeHandle::LEFT_TOP );
01543                 addChild(mResizeHandle[3]);
01544         }
01545         mResizable = can_resize;
01546 }
01547 
01548 void LLFloater::setCanDrag(BOOL can_drag)
01549 {
01550         // if we delete drag handle, we no longer have access to the floater's title
01551         // so just enable/disable it
01552         if (!can_drag && mDragHandle->getEnabled())
01553         {
01554                 mDragHandle->setEnabled(FALSE);
01555         }
01556         else if (can_drag && !mDragHandle->getEnabled())
01557         {
01558                 mDragHandle->setEnabled(TRUE);
01559         }
01560 }
01561 
01562 void LLFloater::updateButtons()
01563 {
01564         S32 button_count = 0;
01565         for (S32 i = 0; i < BUTTON_COUNT; i++)
01566         {
01567                 mButtons[i]->setEnabled(mButtonsEnabled[i]);
01568 
01569                 if (mButtonsEnabled[i] 
01570                         //*HACK: always render close button for hosted floaters
01571                         // so that users don't accidentally hit the button when closing multiple windows
01572                         // in the chatterbox
01573                         || (i == BUTTON_CLOSE && mButtonScale != 1.f))
01574                 {
01575                         button_count++;
01576 
01577                         LLRect btn_rect;
01578                         if (mDragOnLeft)
01579                         {
01580                                 btn_rect.setLeftTopAndSize(
01581                                         LLPANEL_BORDER_WIDTH,
01582                                         getRect().getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count,
01583                                         llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
01584                                         llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
01585                         }
01586                         else
01587                         {
01588                                 btn_rect.setLeftTopAndSize(
01589                                         getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count,
01590                                         getRect().getHeight() - CLOSE_BOX_FROM_TOP,
01591                                         llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
01592                                         llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
01593                         }
01594 
01595                         mButtons[i]->setRect(btn_rect);
01596                         mButtons[i]->setVisible(TRUE);
01597                         // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
01598                         mButtons[i]->setTabStop(i == BUTTON_RESTORE);
01599                 }
01600                 else if (mButtons[i])
01601                 {
01602                         mButtons[i]->setVisible(FALSE);
01603                 }
01604         }
01605         if (mDragHandle)
01606                 mDragHandle->setMaxTitleWidth(getRect().getWidth() - (button_count * (LLFLOATER_CLOSE_BOX_SIZE + 1)));
01607 }
01608 
01609 void LLFloater::buildButtons()
01610 {
01611         for (S32 i = 0; i < BUTTON_COUNT; i++)
01612         {
01613                 LLRect btn_rect;
01614                 if (mDragOnLeft)
01615                 {
01616                         btn_rect.setLeftTopAndSize(
01617                                 LLPANEL_BORDER_WIDTH,
01618                                 getRect().getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
01619                                 llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
01620                                 llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
01621                 }
01622                 else
01623                 {
01624                         btn_rect.setLeftTopAndSize(
01625                                 getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
01626                                 getRect().getHeight() - CLOSE_BOX_FROM_TOP,
01627                                 llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
01628                                 llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
01629                 }
01630 
01631                 LLButton* buttonp = new LLButton(
01632                         sButtonNames[i],
01633                         btn_rect,
01634                         sButtonActiveImageNames[i],
01635                         sButtonPressedImageNames[i],
01636                         "",
01637                         sButtonCallbacks[i],
01638                         this,
01639                         LLFontGL::sSansSerif);
01640 
01641                 buttonp->setTabStop(FALSE);
01642                 buttonp->setFollowsTop();
01643                 buttonp->setFollowsRight();
01644                 buttonp->setToolTip( sButtonToolTips[i] );
01645                 buttonp->setImageColor(LLUI::sColorsGroup->getColor("FloaterButtonImageColor"));
01646                 buttonp->setHoverImages(sButtonPressedImageNames[i],
01647                                                                 sButtonPressedImageNames[i]);
01648                 buttonp->setScaleImage(TRUE);
01649                 buttonp->setSaveToXML(false);
01650                 addChild(buttonp);
01651                 mButtons[i] = buttonp;
01652         }
01653 
01654         updateButtons();
01655 }
01656 
01658 // LLFloaterView
01659 
01660 LLFloaterView::LLFloaterView( const LLString& name, const LLRect& rect )
01661 :       LLUICtrl( name, rect, FALSE, NULL, NULL, FOLLOWS_ALL ),
01662         mFocusCycleMode(FALSE),
01663         mSnapOffsetBottom(0)
01664 {
01665         setTabStop(FALSE);
01666         resetStartingFloaterPosition();
01667 }
01668 
01669 // By default, adjust vertical.
01670 void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
01671 {
01672         reshapeFloater(width, height, called_from_parent, ADJUST_VERTICAL_YES);
01673 }
01674 
01675 // When reshaping this view, make the floaters follow their closest edge.
01676 void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical)
01677 {
01678         S32 old_width = getRect().getWidth();
01679         S32 old_height = getRect().getHeight();
01680 
01681         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
01682         {
01683                 LLView* viewp = *child_it;
01684                 LLFloater* floaterp = (LLFloater*)viewp;
01685                 if (floaterp->isDependent())
01686                 {
01687                         // dependents use same follow flags as their "dependee"
01688                         continue;
01689                 }
01690                 LLRect r = floaterp->getRect();
01691 
01692                 // Compute absolute distance from each edge of screen
01693                 S32 left_offset = llabs(r.mLeft - 0);
01694                 S32 right_offset = llabs(old_width - r.mRight);
01695 
01696                 S32 top_offset = llabs(old_height - r.mTop);
01697                 S32 bottom_offset = llabs(r.mBottom - 0);
01698 
01699                 // Make if follow the edge it is closest to
01700                 U32 follow_flags = 0x0;
01701 
01702                 if (left_offset < right_offset)
01703                 {
01704                         follow_flags |= FOLLOWS_LEFT;
01705                 }
01706                 else
01707                 {
01708                         follow_flags |= FOLLOWS_RIGHT;
01709                 }
01710 
01711                 // "No vertical adjustment" usually means that the bottom of the view
01712                 // has been pushed up or down.  Hence we want the floaters to follow
01713                 // the top.
01714                 if (!adjust_vertical)
01715                 {
01716                         follow_flags |= FOLLOWS_TOP;
01717                 }
01718                 else if (top_offset < bottom_offset)
01719                 {
01720                         follow_flags |= FOLLOWS_TOP;
01721                 }
01722                 else
01723                 {
01724                         follow_flags |= FOLLOWS_BOTTOM;
01725                 }
01726 
01727                 floaterp->setFollows(follow_flags);
01728 
01729                 //RN: all dependent floaters copy follow behavior of "parent"
01730                 for(LLFloater::handle_set_iter_t dependent_it = floaterp->mDependents.begin();
01731                         dependent_it != floaterp->mDependents.end(); ++dependent_it)
01732                 {
01733                         LLFloater* dependent_floaterp = dependent_it->get();
01734                         if (dependent_floaterp)
01735                         {
01736                                 dependent_floaterp->setFollows(follow_flags);
01737                         }
01738                 }
01739         }
01740 
01741         LLView::reshape(width, height, called_from_parent);
01742 }
01743 
01744 
01745 void LLFloaterView::restoreAll()
01746 {
01747         // make sure all subwindows aren't minimized
01748         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
01749         {
01750                 LLFloater* floaterp = (LLFloater*)*child_it;
01751                 floaterp->setMinimized(FALSE);
01752         }
01753 
01754         // *FIX: make sure dependents are restored
01755 
01756         // children then deleted by default view constructor
01757 }
01758 
01759 
01760 void LLFloaterView::getNewFloaterPosition(S32* left,S32* top)
01761 {
01762         // Workaround: mRect may change between when this object is created and the first time it is used.
01763         static BOOL first = TRUE;
01764         if( first )
01765         {
01766                 resetStartingFloaterPosition();
01767                 first = FALSE;
01768         }
01769         
01770         const S32 FLOATER_PAD = 16;
01771         LLCoordWindow window_size;
01772         getWindow()->getSize(&window_size);
01773         LLRect full_window(0, window_size.mY, window_size.mX, 0);
01774         LLRect floater_creation_rect(
01775                 160,
01776                 full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
01777                 full_window.getWidth() * 2 / 3,
01778                 130 );
01779         floater_creation_rect.stretch( -FLOATER_PAD );
01780 
01781         *left = mNextLeft;
01782         *top = mNextTop;
01783 
01784         const S32 STEP = 25;
01785         S32 bottom = floater_creation_rect.mBottom + 2 * STEP;
01786         S32 right = floater_creation_rect.mRight - 4 * STEP;
01787 
01788         mNextTop -= STEP;
01789         mNextLeft += STEP;
01790 
01791         if( (mNextTop < bottom ) || (mNextLeft > right) )
01792         {
01793                 mColumn++;
01794                 mNextTop = floater_creation_rect.mTop;
01795                 mNextLeft = STEP * mColumn;
01796 
01797                 if( (mNextTop < bottom) || (mNextLeft > right) )
01798                 {
01799                         // Advancing the column didn't work, so start back at the beginning
01800                         resetStartingFloaterPosition();
01801                 }
01802         }
01803 }
01804 
01805 void LLFloaterView::resetStartingFloaterPosition()
01806 {
01807         const S32 FLOATER_PAD = 16;
01808         LLCoordWindow window_size;
01809         getWindow()->getSize(&window_size);
01810         LLRect full_window(0, window_size.mY, window_size.mX, 0);
01811         LLRect floater_creation_rect(
01812                 160,
01813                 full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
01814                 full_window.getWidth() * 2 / 3,
01815                 130 );
01816         floater_creation_rect.stretch( -FLOATER_PAD );
01817 
01818         mNextLeft = floater_creation_rect.mLeft;
01819         mNextTop = floater_creation_rect.mTop;
01820         mColumn = 0;
01821 }
01822 
01823 LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
01824 {
01825         LLRect base_rect = reference_floater->getRect();
01826         S32 width = neighbor->getRect().getWidth();
01827         S32 height = neighbor->getRect().getHeight();
01828         LLRect new_rect = neighbor->getRect();
01829 
01830         LLRect expanded_base_rect = base_rect;
01831         expanded_base_rect.stretch(10);
01832         for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
01833                 dependent_it != reference_floater->mDependents.end(); ++dependent_it)
01834         {
01835                 LLFloater* sibling = dependent_it->get();
01836                 // check for dependents within 10 pixels of base floater
01837                 if (sibling && 
01838                         sibling != neighbor && 
01839                         sibling->getVisible() && 
01840                         expanded_base_rect.rectInRect(&sibling->getRect()))
01841                 {
01842                         base_rect.unionWith(sibling->getRect());
01843                 }
01844         }
01845 
01846         S32 left_margin = llmax(0, base_rect.mLeft);
01847         S32 right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
01848         S32 top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
01849         S32 bottom_margin = llmax(0, base_rect.mBottom);
01850 
01851         // find position for floater in following order
01852         // right->left->bottom->top
01853         for (S32 i = 0; i < 5; i++)
01854         {
01855                 if (right_margin > width)
01856                 {
01857                         new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop);
01858                         return new_rect;
01859                 }
01860                 else if (left_margin > width)
01861                 {
01862                         new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop);
01863                         return new_rect;
01864                 }
01865                 else if (bottom_margin > height)
01866                 {
01867                         new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop);
01868                         return new_rect;
01869                 }
01870                 else if (top_margin > height)
01871                 {
01872                         new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom);
01873                         return new_rect;
01874                 }
01875 
01876                 // keep growing margins to find "best" fit
01877                 left_margin += 20;
01878                 right_margin += 20;
01879                 top_margin += 20;
01880                 bottom_margin += 20;
01881         }
01882 
01883         // didn't find anything, return initial rect
01884         return new_rect;
01885 }
01886 
01887 
01888 void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
01889 {
01890         // *TODO: make this respect floater's mAutoFocus value, instead of
01891         // using parameter
01892         if (child->getHost())
01893         {
01894                 // this floater is hosted elsewhere and hence not one of our children, abort
01895                 return;
01896         }
01897         std::vector<LLView*> floaters_to_move;
01898         // Look at all floaters...tab
01899         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
01900         {
01901                 LLView* viewp = *child_it;
01902                 LLFloater *floater = (LLFloater *)viewp;
01903 
01904                 // ...but if I'm a dependent floater...
01905                 if (child->isDependent())
01906                 {
01907                         // ...look for floaters that have me as a dependent...
01908                         LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle());
01909 
01910                         if (found_dependent != floater->mDependents.end())
01911                         {
01912                                 // ...and make sure all children of that floater (including me) are brought to front...
01913                                 for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
01914                                         dependent_it != floater->mDependents.end(); )
01915                                 {
01916                                         LLFloater* sibling = dependent_it->get();
01917                                         if (sibling)
01918                                         {
01919                                                 floaters_to_move.push_back(sibling);
01920                                         }
01921                                         ++dependent_it;
01922                                 }
01923                                 //...before bringing my parent to the front...
01924                                 floaters_to_move.push_back(floater);
01925                         }
01926                 }
01927         }
01928 
01929         std::vector<LLView*>::iterator view_it;
01930         for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
01931         {
01932                 LLFloater* floaterp = (LLFloater*)(*view_it);
01933                 sendChildToFront(floaterp);
01934 
01935                 // always unminimize dependee, but allow dependents to stay minimized
01936                 if (!floaterp->isDependent())
01937                 {
01938                         floaterp->setMinimized(FALSE);
01939                 }
01940         }
01941         floaters_to_move.clear();
01942 
01943         // ...then bringing my own dependents to the front...
01944         for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
01945                 dependent_it != child->mDependents.end(); )
01946         {
01947                 LLFloater* dependent = dependent_it->get();
01948                 if (dependent)
01949                 {
01950                         sendChildToFront(dependent);
01951                         //don't un-minimize dependent windows automatically
01952                         // respect user's wishes
01953                         //dependent->setMinimized(FALSE);
01954                 }
01955                 ++dependent_it;
01956         }
01957 
01958         // ...and finally bringing myself to front 
01959         // (do this last, so that I'm left in front at end of this call)
01960         if( *getChildList()->begin() != child ) 
01961         {
01962                 sendChildToFront(child);
01963         }
01964         child->setMinimized(FALSE);
01965         if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
01966         {
01967                 child->setFocus(TRUE);
01968         }
01969 }
01970 
01971 void LLFloaterView::highlightFocusedFloater()
01972 {
01973         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
01974         {
01975                 LLFloater *floater = (LLFloater *)(*child_it);
01976 
01977                 // skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
01978                 if (floater->isDependent())
01979                 {
01980                         continue;
01981                 }
01982 
01983                 BOOL floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
01984                 for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
01985                         dependent_it != floater->mDependents.end(); 
01986                         ++dependent_it)
01987                 {
01988                         LLFloater* dependent_floaterp = dependent_it->get();
01989                         if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
01990                         {
01991                                 floater_or_dependent_has_focus = TRUE;
01992                         }
01993                 }
01994 
01995                 // now set this floater and all its dependents
01996                 floater->setForeground(floater_or_dependent_has_focus);
01997 
01998                 for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
01999                         dependent_it != floater->mDependents.end(); )
02000                 {
02001                         LLFloater* dependent_floaterp = dependent_it->get();
02002                         if (dependent_floaterp)
02003                         {
02004                                 dependent_floaterp->setForeground(floater_or_dependent_has_focus);
02005                         }
02006                         ++dependent_it;
02007                 }
02008                         
02009                 floater->cleanupHandles();
02010         }
02011 }
02012 
02013 void LLFloaterView::unhighlightFocusedFloater()
02014 {
02015         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02016         {
02017                 LLFloater *floater = (LLFloater *)(*child_it);
02018 
02019                 floater->setForeground(FALSE);
02020         }
02021 }
02022 
02023 void LLFloaterView::focusFrontFloater()
02024 {
02025         LLFloater* floaterp = getFrontmost();
02026         if (floaterp)
02027         {
02028                 floaterp->setFocus(TRUE);
02029         }
02030 }
02031 
02032 void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
02033 {
02034         S32 col = 0;
02035         LLRect snap_rect_local = getLocalSnapRect();
02036         for(S32 row = snap_rect_local.mBottom;
02037                 row < snap_rect_local.getHeight() - LLFLOATER_HEADER_SIZE;
02038                 row += LLFLOATER_HEADER_SIZE ) //loop rows
02039         {
02040                 for(col = snap_rect_local.mLeft;
02041                         col < snap_rect_local.getWidth() - MINIMIZED_WIDTH;
02042                         col += MINIMIZED_WIDTH)
02043                 {
02044                         bool foundGap = TRUE;
02045                         for(child_list_const_iter_t child_it = getChildList()->begin();
02046                                 child_it != getChildList()->end();
02047                                 ++child_it) //loop floaters
02048                         {
02049                                 // Examine minimized children.
02050                                 LLFloater* floater = (LLFloater*)((LLView*)*child_it);
02051                                 if(floater->isMinimized()) 
02052                                 {
02053                                         LLRect r = floater->getRect();
02054                                         if((r.mBottom < (row + LLFLOATER_HEADER_SIZE))
02055                                            && (r.mBottom > (row - LLFLOATER_HEADER_SIZE))
02056                                            && (r.mLeft < (col + MINIMIZED_WIDTH))
02057                                            && (r.mLeft > (col - MINIMIZED_WIDTH)))
02058                                         {
02059                                                 // needs the check for off grid. can't drag,
02060                                                 // but window resize makes them off
02061                                                 foundGap = FALSE;
02062                                                 break;
02063                                         }
02064                                 }
02065                         } //done floaters
02066                         if(foundGap)
02067                         {
02068                                 *left = col;
02069                                 *bottom = row;
02070                                 return; //done
02071                         }
02072                 } //done this col
02073         }
02074 
02075         // crude - stack'em all at 0,0 when screen is full of minimized
02076         // floaters.
02077         *left = snap_rect_local.mLeft;
02078         *bottom = snap_rect_local.mBottom;
02079 }
02080 
02081 
02082 void LLFloaterView::destroyAllChildren()
02083 {
02084         LLView::deleteAllChildren();
02085 }
02086 
02087 void LLFloaterView::closeAllChildren(bool app_quitting)
02088 {
02089         // iterate over a copy of the list, because closing windows will destroy
02090         // some windows on the list.
02091         child_list_t child_list = *(getChildList());
02092 
02093         for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
02094         {
02095                 LLView* viewp = *it;
02096                 child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
02097                 if (exists == getChildList()->end())
02098                 {
02099                         // this floater has already been removed
02100                         continue;
02101                 }
02102 
02103                 LLFloater* floaterp = (LLFloater*)viewp;
02104 
02105                 // Attempt to close floater.  This will cause the "do you want to save"
02106                 // dialogs to appear.
02107                 if (floaterp->canClose() && !floaterp->isDead())
02108                 {
02109                         floaterp->close(app_quitting);
02110                 }
02111         }
02112 }
02113 
02114 
02115 BOOL LLFloaterView::allChildrenClosed()
02116 {
02117         // see if there are any visible floaters (some floaters "close"
02118         // by setting themselves invisible)
02119         for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
02120         {
02121                 LLView* viewp = *it;
02122                 LLFloater* floaterp = (LLFloater*)viewp;
02123 
02124                 if (floaterp->getVisible() && !floaterp->isDead() && floaterp->canClose())
02125                 {
02126                         return false;
02127                 }
02128         }
02129         return true;
02130 }
02131 
02132 
02133 void LLFloaterView::refresh()
02134 {
02135         // Constrain children to be entirely on the screen
02136         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02137         {
02138                 LLFloater* floaterp = (LLFloater*)*child_it;
02139                 if( floaterp->getVisible() )
02140                 {
02141                         // minimized floaters are kept fully onscreen
02142                         adjustToFitScreen(floaterp, !floaterp->isMinimized());
02143                 }
02144         }
02145 }
02146 
02147 void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside)
02148 {
02149         if (floater->getParent() != this)
02150         {
02151                 // floater is hosted elsewhere, so ignore
02152                 return;
02153         }
02154         S32 screen_width = getSnapRect().getWidth();
02155         S32 screen_height = getSnapRect().getHeight();
02156         // convert to local coordinate frame
02157         LLRect snap_rect_local = getLocalSnapRect();
02158 
02159         if( floater->isResizable() )
02160         {
02161                 LLRect view_rect = floater->getRect();
02162                 S32 old_width = view_rect.getWidth();
02163                 S32 old_height = view_rect.getHeight();
02164                 S32 min_width;
02165                 S32 min_height;
02166                 floater->getResizeLimits( &min_width, &min_height );
02167 
02168                 // Make sure floater isn't already smaller than its min height/width?
02169                 S32 new_width = llmax( min_width, old_width );
02170                 S32 new_height = llmax( min_height, old_height);
02171 
02172                 if((new_width > screen_width) || (new_height > screen_height))
02173                 {
02174                         // We have to make this window able to fit on screen
02175                         new_width = llmin(new_width, screen_width);
02176                         new_height = llmin(new_height, screen_height);
02177 
02178                         // ...while respecting minimum width/height
02179                         new_width = llmax(new_width, min_width);
02180                         new_height = llmax(new_height, min_height);
02181 
02182                         floater->reshape( new_width, new_height, TRUE );
02183                         if (floater->followsRight())
02184                         {
02185                                 floater->translate(old_width - new_width, 0);
02186                         }
02187 
02188                         if (floater->followsTop())
02189                         {
02190                                 floater->translate(0, old_height - new_height);
02191                         }
02192                 }
02193         }
02194 
02195         // move window fully onscreen
02196         if (floater->translateIntoRect( snap_rect_local, allow_partial_outside ))
02197         {
02198                 floater->clearSnapTarget();
02199         }
02200 }
02201 
02202 void LLFloaterView::draw()
02203 {
02204         refresh();
02205 
02206         // hide focused floater if in cycle mode, so that it can be drawn on top
02207         LLFloater* focused_floater = getFocusedFloater();
02208 
02209         if (mFocusCycleMode && focused_floater)
02210         {
02211                 child_list_const_iter_t child_it = getChildList()->begin();
02212                 for (;child_it != getChildList()->end(); ++child_it)
02213                 {
02214                         if ((*child_it) != focused_floater)
02215                         {
02216                                 drawChild(*child_it);
02217                         }
02218                 }
02219 
02220                 drawChild(focused_floater, -TABBED_FLOATER_OFFSET, TABBED_FLOATER_OFFSET);
02221         }
02222         else
02223         {
02224                 LLView::draw();
02225         }
02226 }
02227 
02228 LLRect LLFloaterView::getSnapRect() const
02229 {
02230         LLRect snap_rect = getRect();
02231         snap_rect.mBottom += mSnapOffsetBottom;
02232 
02233         return snap_rect;
02234 }
02235 
02236 LLFloater *LLFloaterView::getFocusedFloater()
02237 {
02238         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02239         {
02240                 LLUICtrl* ctrlp = (*child_it)->isCtrl() ? static_cast<LLUICtrl*>(*child_it) : NULL;
02241                 if ( ctrlp && ctrlp->hasFocus() )
02242                 {
02243                         return static_cast<LLFloater *>(ctrlp);
02244                 }
02245         }
02246         return NULL;
02247 }
02248 
02249 LLFloater *LLFloaterView::getFrontmost()
02250 {
02251         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02252         {
02253                 LLView* viewp = *child_it;
02254                 if ( viewp->getVisible() && !viewp->isDead())
02255                 {
02256                         return (LLFloater *)viewp;
02257                 }
02258         }
02259         return NULL;
02260 }
02261 
02262 LLFloater *LLFloaterView::getBackmost()
02263 {
02264         LLFloater* back_most = NULL;
02265         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02266         {
02267                 LLView* viewp = *child_it;
02268                 if ( viewp->getVisible() )
02269                 {
02270                         back_most = (LLFloater *)viewp;
02271                 }
02272         }
02273         return back_most;
02274 }
02275 
02276 void LLFloaterView::syncFloaterTabOrder()
02277 {
02278         // bring focused floater to front
02279         for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
02280         {
02281                 LLFloater* floaterp = (LLFloater*)*child_it;
02282                 if (gFocusMgr.childHasKeyboardFocus(floaterp))
02283                 {
02284                         bringToFront(floaterp, FALSE);
02285                         break;
02286                 }
02287         }
02288 
02289         // then sync draw order to tab order
02290         for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
02291         {
02292                 LLFloater* floaterp = (LLFloater*)*child_it;
02293                 moveChildToFrontOfTabGroup(floaterp);
02294         }
02295 }
02296 
02297 LLFloater*      LLFloaterView::getParentFloater(LLView* viewp)
02298 {
02299         LLView* parentp = viewp->getParent();
02300 
02301         while(parentp && parentp != this)
02302         {
02303                 viewp = parentp;
02304                 parentp = parentp->getParent();
02305         }
02306 
02307         if (parentp == this)
02308         {
02309                 return (LLFloater*)viewp;
02310         }
02311 
02312         return NULL;
02313 }
02314 
02315 S32 LLFloaterView::getZOrder(LLFloater* child)
02316 {
02317         S32 rv = 0;
02318         for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
02319         {
02320                 LLView* viewp = *child_it;
02321                 if(viewp == child)
02322                 {
02323                         break;
02324                 }
02325                 ++rv;
02326         }
02327         return rv;
02328 }
02329 
02330 void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list)
02331 {
02332         for (child_list_const_iter_t child_iter = getChildList()->begin();
02333                  child_iter != getChildList()->end(); ++child_iter)
02334         {
02335                 LLView *view = *child_iter;
02336                 if (skip_list.find(view) == skip_list.end())
02337                 {
02338                         view->pushVisible(visible);
02339                 }
02340         }
02341 }
02342 
02343 void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
02344 {
02345         for (child_list_const_iter_t child_iter = getChildList()->begin();
02346                  child_iter != getChildList()->end(); ++child_iter)
02347         {
02348                 LLView *view = *child_iter;
02349                 if (skip_list.find(view) == skip_list.end())
02350                 {
02351                         view->popVisible();
02352                 }
02353         }
02354 }
02355 
02356 //
02357 // LLMultiFloater
02358 //
02359 
02360 LLMultiFloater::LLMultiFloater() :
02361         mTabContainer(NULL),
02362         mTabPos(LLTabContainer::TOP),
02363         mAutoResize(TRUE),
02364         mOrigMinWidth(0),
02365         mOrigMinHeight(0)
02366 {
02367 
02368 }
02369 
02370 LLMultiFloater::LLMultiFloater(LLTabContainer::TabPosition tab_pos) :
02371         mTabContainer(NULL),
02372         mTabPos(tab_pos),
02373         mAutoResize(TRUE),
02374         mOrigMinWidth(0),
02375         mOrigMinHeight(0)
02376 {
02377 
02378 }
02379 
02380 LLMultiFloater::LLMultiFloater(const LLString &name) :
02381         LLFloater(name),
02382         mTabContainer(NULL),
02383         mTabPos(LLTabContainer::TOP),
02384         mAutoResize(FALSE),
02385         mOrigMinWidth(0),
02386         mOrigMinHeight(0)
02387 {
02388 }
02389 
02390 LLMultiFloater::LLMultiFloater(
02391         const LLString& name,
02392         const LLRect& rect,
02393         LLTabContainer::TabPosition tab_pos,
02394         BOOL auto_resize) : 
02395         LLFloater(name, rect, name),
02396         mTabContainer(NULL),
02397         mTabPos(LLTabContainer::TOP),
02398         mAutoResize(auto_resize),
02399         mOrigMinWidth(0),
02400         mOrigMinHeight(0)
02401 {
02402         mTabContainer = new LLTabContainer("Preview Tabs", 
02403                 LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - LLFLOATER_HEADER_SIZE, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0), 
02404                 mTabPos, 
02405                 FALSE, 
02406                 FALSE);
02407         mTabContainer->setFollowsAll();
02408         if (isResizable())
02409         {
02410                 mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
02411         }
02412 
02413         addChild(mTabContainer);
02414 }
02415 
02416 LLMultiFloater::LLMultiFloater(
02417         const LLString& name,
02418         const LLString& rect_control,
02419         LLTabContainer::TabPosition tab_pos,
02420         BOOL auto_resize) : 
02421         LLFloater(name, rect_control, name),
02422         mTabContainer(NULL),
02423         mTabPos(tab_pos),
02424         mAutoResize(auto_resize),
02425         mOrigMinWidth(0),
02426         mOrigMinHeight(0)
02427 {
02428         mTabContainer = new LLTabContainer("Preview Tabs", 
02429                 LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - LLFLOATER_HEADER_SIZE, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0), 
02430                 mTabPos, 
02431                 FALSE, 
02432                 FALSE);
02433         mTabContainer->setFollowsAll();
02434         if (isResizable() && mTabPos == LLTabContainer::BOTTOM)
02435         {
02436                 mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
02437         }
02438 
02439         addChild(mTabContainer);
02440         
02441 }
02442 
02443 
02444 void LLMultiFloater::open()     /* Flawfinder: ignore */
02445 {
02446         if (mTabContainer->getTabCount() > 0)
02447         {
02448                 LLFloater::open();      /* Flawfinder: ignore */
02449         }
02450         else
02451         {
02452                 // for now, don't allow multifloaters
02453                 // without any child floaters
02454                 close();
02455         }
02456 }
02457 
02458 void LLMultiFloater::onClose(bool app_quitting)
02459 {
02460         if(closeAllFloaters() == TRUE)
02461         {
02462                 LLFloater::onClose(app_quitting);
02463         }//else not all tabs could be closed...
02464 }
02465 
02466 void LLMultiFloater::draw()
02467 {
02468         if (mTabContainer->getTabCount() == 0)
02469         {
02470                 //RN: could this potentially crash in draw hierarchy?
02471                 close();
02472         }
02473         else
02474         {
02475                 for (S32 i = 0; i < mTabContainer->getTabCount(); i++)
02476                 {
02477                         LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i);
02478                         if (floaterp->getShortTitle() != mTabContainer->getPanelTitle(i))
02479                         {
02480                                 mTabContainer->setPanelTitle(i, floaterp->getShortTitle());
02481                         }
02482                 }
02483                 LLFloater::draw();
02484         }
02485 }
02486 
02487 BOOL LLMultiFloater::closeAllFloaters()
02488 {
02489         S32     tabToClose = 0;
02490         S32     lastTabCount = mTabContainer->getTabCount();
02491         while (tabToClose < mTabContainer->getTabCount())
02492         {
02493                 LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
02494                 first_floater->close();
02495                 if(lastTabCount == mTabContainer->getTabCount())
02496                 {
02497                         //Tab did not actually close, possibly due to a pending Save Confirmation dialog..
02498                         //so try and close the next one in the list...
02499                         tabToClose++;
02500                 }else
02501                 {
02502                         //Tab closed ok.
02503                         lastTabCount = mTabContainer->getTabCount();
02504                 }
02505         }
02506         if( mTabContainer->getTabCount() != 0 )
02507                 return FALSE; // Couldn't close all the tabs (pending save dialog?) so return FALSE.
02508         return TRUE; //else all tabs were successfully closed...
02509 }
02510 
02511 void LLMultiFloater::growToFit(S32 content_width, S32 content_height)
02512 {
02513         S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
02514         S32 new_height = llmax(getRect().getHeight(), content_height + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
02515 
02516     if (isMinimized())
02517     {
02518         LLRect newrect;
02519         newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height);
02520         setExpandedRect(newrect);
02521     }
02522         else
02523         {
02524                 S32 old_height = getRect().getHeight();
02525                 reshape(new_width, new_height);
02526                 // keep top left corner in same position
02527                 translate(0, old_height - new_height);
02528         }
02529 }
02530 
02542 void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
02543 {
02544         if (!floaterp)
02545         {
02546                 return;
02547         }
02548 
02549         if (!mTabContainer)
02550         {
02551                 llerrs << "Tab Container used without having been initialized." << llendl;
02552                 return;
02553         }
02554 
02555         if (floaterp->getHost() == this)
02556         {
02557                 // already hosted by me, remove
02558                 // do this so we get updated title, etc.
02559                 mFloaterDataMap.erase(floaterp->getHandle());
02560                 mTabContainer->removeTabPanel(floaterp);
02561         }
02562         else if (floaterp->getHost())
02563         {
02564                 // floaterp is hosted by somebody else and
02565                 // this is adding it, so remove it from it's old host
02566                 floaterp->getHost()->removeFloater(floaterp);
02567         }
02568         else if (floaterp->getParent() == gFloaterView)
02569         {
02570                 // rehost preview floater as child panel
02571                 gFloaterView->removeChild(floaterp);
02572         }
02573 
02574         // store original configuration
02575         LLFloaterData floater_data;
02576         floater_data.mWidth = floaterp->getRect().getWidth();
02577         floater_data.mHeight = floaterp->getRect().getHeight();
02578         floater_data.mCanMinimize = floaterp->isMinimizeable();
02579         floater_data.mCanResize = floaterp->isResizable();
02580 
02581         // remove minimize and close buttons
02582         floaterp->setCanMinimize(FALSE);
02583         floaterp->setCanResize(FALSE);
02584         floaterp->setCanDrag(FALSE);
02585         floaterp->storeRectControl();
02586         // avoid double rendering of floater background (makes it more opaque)
02587         floaterp->setBackgroundVisible(FALSE);
02588 
02589         if (mAutoResize)
02590         {
02591                 growToFit(floater_data.mWidth, floater_data.mHeight);
02592         }
02593 
02594         //add the panel, add it to proper maps
02595         mTabContainer->addTabPanel(floaterp, floaterp->getShortTitle(), FALSE, onTabSelected, this, 0, FALSE, insertion_point);
02596         mFloaterDataMap[floaterp->getHandle()] = floater_data;
02597 
02598         updateResizeLimits();
02599 
02600         if ( select_added_floater )
02601         {
02602                 mTabContainer->selectTabPanel(floaterp);
02603         }
02604         else
02605         {
02606                 // reassert visible tab (hiding new floater if necessary)
02607                 mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex());
02608         }
02609 
02610         floaterp->setHost(this);
02611         if (isMinimized())
02612         {
02613                 floaterp->setVisible(FALSE);
02614         }
02615 }
02616 
02625 BOOL LLMultiFloater::selectFloater(LLFloater* floaterp)
02626 {
02627         return mTabContainer->selectTabPanel(floaterp);
02628 }
02629 
02630 // virtual
02631 void LLMultiFloater::selectNextFloater()
02632 {
02633         mTabContainer->selectNextTab();
02634 }
02635 
02636 // virtual
02637 void LLMultiFloater::selectPrevFloater()
02638 {
02639         mTabContainer->selectPrevTab();
02640 }
02641 
02642 void LLMultiFloater::showFloater(LLFloater* floaterp)
02643 {
02644         // we won't select a panel that already is selected
02645         // it is hard to do this internally to tab container
02646         // as tab selection is handled via index and the tab at a given
02647         // index might have changed
02648         if (floaterp != mTabContainer->getCurrentPanel() &&
02649                 !mTabContainer->selectTabPanel(floaterp))
02650         {
02651                 addFloater(floaterp, TRUE);
02652         }
02653 }
02654 
02655 void LLMultiFloater::removeFloater(LLFloater* floaterp)
02656 {
02657         if ( floaterp->getHost() != this )
02658                 return;
02659 
02660         floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
02661         if (found_data_it != mFloaterDataMap.end())
02662         {
02663                 LLFloaterData& floater_data = found_data_it->second;
02664                 floaterp->setCanMinimize(floater_data.mCanMinimize);
02665                 if (!floater_data.mCanResize)
02666                 {
02667                         // restore original size
02668                         floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
02669                 }
02670                 floaterp->setCanResize(floater_data.mCanResize);
02671                 mFloaterDataMap.erase(found_data_it);
02672         }
02673         mTabContainer->removeTabPanel(floaterp);
02674         floaterp->setBackgroundVisible(TRUE);
02675         floaterp->setCanDrag(TRUE);
02676         floaterp->setHost(NULL);
02677         floaterp->applyRectControl();
02678 
02679         updateResizeLimits();
02680 
02681         tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
02682 }
02683 
02684 void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
02685 {
02686         // default implementation does nothing
02687 }
02688 
02689 void LLMultiFloater::tabClose()
02690 {
02691         if (mTabContainer->getTabCount() == 0)
02692         {
02693                 // no more children, close myself
02694                 close();
02695         }
02696 }
02697 
02698 void LLMultiFloater::setVisible(BOOL visible)
02699 {
02700         // *FIX: shouldn't have to do this, fix adding to minimized multifloater
02701         LLFloater::setVisible(visible);
02702         
02703         if (mTabContainer)
02704         {
02705                 LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
02706 
02707                 if (cur_floaterp)
02708                 {
02709                         cur_floaterp->setVisible(visible);
02710                 }
02711 
02712                 // if no tab selected, and we're being shown,
02713                 // select last tab to be added
02714                 if (visible && !cur_floaterp)
02715                 {
02716                         mTabContainer->selectLastTab();
02717                 }
02718         }
02719 }
02720 
02721 BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)
02722 {
02723         if (key == 'W' && mask == MASK_CONTROL)
02724         {
02725                 LLFloater* floater = getActiveFloater();
02726                 // is user closeable and is system closeable
02727                 if (floater && floater->canClose() && floater->isCloseable())
02728                 {
02729                         floater->close();
02730                 }
02731                 return TRUE;
02732         }
02733 
02734         return LLFloater::handleKeyHere(key, mask);
02735 }
02736 
02737 LLFloater* LLMultiFloater::getActiveFloater()
02738 {
02739         return (LLFloater*)mTabContainer->getCurrentPanel();
02740 }
02741 
02742 S32     LLMultiFloater::getFloaterCount()
02743 {
02744         return mTabContainer->getTabCount();
02745 }
02746 
02756 BOOL LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
02757 {
02758         if ( floaterp && floaterp->getHost() == this )
02759                 return mTabContainer->getTabPanelFlashing(floaterp);
02760 
02761         return FALSE;
02762 }
02763 
02773 void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
02774 {
02775         if ( floaterp && floaterp->getHost() == this )
02776                 mTabContainer->setTabPanelFlashing(floaterp, flashing);
02777 }
02778 
02779 //static
02780 void LLMultiFloater::onTabSelected(void* userdata, bool from_click)
02781 {
02782         LLMultiFloater* floaterp = (LLMultiFloater*)userdata;
02783 
02784         floaterp->tabOpen((LLFloater*)floaterp->mTabContainer->getCurrentPanel(), from_click);
02785 }
02786 
02787 void LLMultiFloater::setCanResize(BOOL can_resize)
02788 {
02789         LLFloater::setCanResize(can_resize);
02790         if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
02791         {
02792                 mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
02793         }
02794         else
02795         {
02796                 mTabContainer->setRightTabBtnOffset(0);
02797         }
02798 }
02799 
02800 BOOL LLMultiFloater::postBuild()
02801 {
02802         // remember any original xml minimum size
02803         getResizeLimits(&mOrigMinWidth, &mOrigMinHeight);
02804 
02805         if (mTabContainer)
02806         {
02807                 return TRUE;
02808         }
02809 
02810         requires<LLTabContainer>("Preview Tabs");
02811         if (checkRequirements())
02812         {
02813                 mTabContainer = getChild<LLTabContainer>("Preview Tabs");
02814                 return TRUE;
02815         }
02816 
02817         return FALSE;
02818 }
02819 
02820 void LLMultiFloater::updateResizeLimits()
02821 {
02822         // initialize minimum size constraint to the original xml values.
02823         S32 new_min_width = mOrigMinWidth;
02824         S32 new_min_height = mOrigMinHeight;
02825         // possibly increase minimum size constraint due to children's minimums.
02826         for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
02827         {
02828                 LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx);
02829                 if (floaterp)
02830                 {
02831                         new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2);
02832                         new_min_height = llmax(new_min_height, floaterp->getMinHeight() + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
02833                 }
02834         }
02835         setResizeLimits(new_min_width, new_min_height);
02836 
02837         S32 cur_height = getRect().getHeight();
02838         S32 new_width = llmax(getRect().getWidth(), new_min_width);
02839         S32 new_height = llmax(getRect().getHeight(), new_min_height);
02840 
02841         if (isMinimized())
02842         {
02843                 const LLRect& expanded = getExpandedRect();
02844                 LLRect newrect;
02845                 newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height));
02846                 setExpandedRect(newrect);
02847         }
02848         else
02849         {
02850                 reshape(new_width, new_height);
02851 
02852                 // make sure upper left corner doesn't move
02853                 translate(0, cur_height - getRect().getHeight());
02854 
02855                 // make sure this window is visible on screen when it has been modified
02856                 // (tab added, etc)
02857                 gFloaterView->adjustToFitScreen(this, TRUE);
02858         }
02859 }
02860 
02861 // virtual
02862 LLXMLNodePtr LLFloater::getXML(bool save_children) const
02863 {
02864         LLXMLNodePtr node = LLPanel::getXML();
02865 
02866         node->createChild("title", TRUE)->setStringValue(getTitle());
02867 
02868         node->createChild("can_resize", TRUE)->setBoolValue(isResizable());
02869 
02870         node->createChild("can_minimize", TRUE)->setBoolValue(isMinimizeable());
02871 
02872         node->createChild("can_close", TRUE)->setBoolValue(isCloseable());
02873 
02874         node->createChild("can_drag_on_left", TRUE)->setBoolValue(isDragOnLeft());
02875 
02876         node->createChild("min_width", TRUE)->setIntValue(getMinWidth());
02877 
02878         node->createChild("min_height", TRUE)->setIntValue(getMinHeight());
02879 
02880         node->createChild("can_tear_off", TRUE)->setBoolValue(mCanTearOff);
02881         
02882         return node;
02883 }
02884 
02885 // static
02886 LLView* LLFloater::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
02887 {
02888         LLString name("floater");
02889         node->getAttributeString("name", name);
02890 
02891         LLFloater *floaterp = new LLFloater(name);
02892 
02893         LLString filename;
02894         node->getAttributeString("filename", filename);
02895 
02896         if (filename.empty())
02897         {
02898                 // Load from node
02899                 floaterp->initFloaterXML(node, parent, factory);
02900         }
02901         else
02902         {
02903                 // Load from file
02904                 factory->buildFloater(floaterp, filename);
02905         }
02906 
02907         return floaterp;
02908 }
02909 
02910 void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory, BOOL open)  /* Flawfinder: ignore */
02911 {
02912         LLString name(getName());
02913         LLString title(getTitle());
02914         LLString short_title(getShortTitle());
02915         LLString rect_control("");
02916         BOOL resizable = isResizable();
02917         S32 min_width = getMinWidth();
02918         S32 min_height = getMinHeight();
02919         BOOL drag_on_left = isDragOnLeft();
02920         BOOL minimizable = isMinimizeable();
02921         BOOL close_btn = isCloseable();
02922         LLRect rect;
02923 
02924         node->getAttributeString("name", name);
02925         node->getAttributeString("title", title);
02926         node->getAttributeString("short_title", short_title);
02927         node->getAttributeString("rect_control", rect_control);
02928         node->getAttributeBOOL("can_resize", resizable);
02929         node->getAttributeBOOL("can_minimize", minimizable);
02930         node->getAttributeBOOL("can_close", close_btn);
02931         node->getAttributeBOOL("can_drag_on_left", drag_on_left);
02932         node->getAttributeS32("min_width", min_width);
02933         node->getAttributeS32("min_height", min_height);
02934 
02935         if (! rect_control.empty())
02936         {
02937                 setRectControl(rect_control);
02938         }
02939 
02940         createRect(node, rect, parent, LLRect());
02941         
02942         setRect(rect);
02943         setName(name);
02944         
02945         initFloater(title,
02946                         resizable,
02947                         min_width,
02948                         min_height,
02949                         drag_on_left,
02950                         minimizable,
02951                         close_btn);
02952 
02953         setShortTitle(short_title);
02954 
02955         BOOL can_tear_off;
02956         if (node->getAttributeBOOL("can_tear_off", can_tear_off))
02957         {
02958                 setCanTearOff(can_tear_off);
02959         }
02960         
02961         initFromXML(node, parent);
02962 
02963         LLMultiFloater* last_host = LLFloater::getFloaterHost();
02964         if (node->hasName("multi_floater"))
02965         {
02966                 LLFloater::setFloaterHost((LLMultiFloater*) this);
02967         }
02968 
02969         initChildrenXML(node, factory);
02970 
02971         if (node->hasName("multi_floater"))
02972         {
02973                 LLFloater::setFloaterHost(last_host);
02974         }
02975 
02976         BOOL result = postBuild();
02977 
02978         if (!result)
02979         {
02980                 llerrs << "Failed to construct floater " << name << llendl;
02981         }
02982 
02983         applyRectControl();
02984         if (open)       /* Flawfinder: ignore */
02985         {
02986                 this->open();   /* Flawfinder: ignore */
02987         }
02988 
02989         moveResizeHandlesToFront();
02990 }

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