summaryrefslogtreecommitdiff
path: root/indra/llui/llfloater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llfloater.cpp')
-rw-r--r--indra/llui/llfloater.cpp2933
1 files changed, 2933 insertions, 0 deletions
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
new file mode 100644
index 0000000000..3f9139fe86
--- /dev/null
+++ b/indra/llui/llfloater.cpp
@@ -0,0 +1,2933 @@
+/**
+ * @file llfloater.cpp
+ * @brief LLFloater base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+#include "linden_common.h"
+
+#include "llfloater.h"
+
+#include "llfocusmgr.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llmenugl.h" // MENU_BAR_HEIGHT
+#include "lltextbox.h"
+#include "llresmgr.h"
+#include "llui.h"
+#include "llviewborder.h"
+#include "llwindow.h"
+#include "llstl.h"
+#include "llcontrol.h"
+#include "lltabcontainer.h"
+#include "v2math.h"
+
+extern BOOL gNoRender;
+
+const S32 MINIMIZED_WIDTH = 160;
+const S32 CLOSE_BOX_FROM_TOP = 1;
+
+LLString LLFloater::sButtonActiveImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnCloseActiveUUID", //BUTTON_CLOSE
+ "UIImgBtnRestoreActiveUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizeActiveUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffActiveUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnCloseActiveUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonInactiveImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnCloseInactiveUUID", //BUTTON_CLOSE
+ "UIImgBtnRestoreInactiveUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizeInactiveUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffInactiveUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnCloseInactiveUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonPressedImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnClosePressedUUID", //BUTTON_CLOSE
+ "UIImgBtnRestorePressedUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizePressedUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffPressedUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnClosePressedUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonNames[BUTTON_COUNT] =
+{
+ "llfloater_close_btn", //BUTTON_CLOSE
+ "llfloater_restore_btn", //BUTTON_RESTORE
+ "llfloater_minimize_btn", //BUTTON_MINIMIZE
+ "llfloater_tear_off_btn", //BUTTON_TEAR_OFF
+ "llfloater_edit_btn", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonToolTips[BUTTON_COUNT] =
+{
+#ifdef LL_DARWIN
+ "Close (Cmd-W)", //BUTTON_CLOSE
+#else
+ "Close (Ctrl-W)", //BUTTON_CLOSE
+#endif
+ "Restore", //BUTTON_RESTORE
+ "Minimize", //BUTTON_MINIMIZE
+ "Tear Off", //BUTTON_TEAR_OFF
+ "Edit", //BUTTON_EDIT
+};
+
+LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
+{
+ LLFloater::onClickClose, //BUTTON_CLOSE
+ LLFloater::onClickMinimize, //BUTTON_RESTORE
+ LLFloater::onClickMinimize, //BUTTON_MINIMIZE
+ LLFloater::onClickTearOff, //BUTTON_TEAR_OFF
+ LLFloater::onClickEdit, //BUTTON_EDIT
+};
+
+LLMultiFloater* LLFloater::sHostp = NULL;
+BOOL LLFloater::sEditModeEnabled;
+LLFloater::handle_map_t LLFloater::sFloaterMap;
+
+LLFloaterView* gFloaterView = NULL;
+
+LLFloater::LLFloater()
+{
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ mDragHandle = NULL;
+}
+
+LLFloater::LLFloater(const LLString& name)
+: LLPanel(name)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+
+ LLString title; // null string
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init(title, FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, TRUE, TRUE); // defaults
+}
+
+
+LLFloater::LLFloater(const LLString& name, const LLRect& rect, const LLString& title,
+ BOOL resizable,
+ S32 min_width,
+ S32 min_height,
+ BOOL drag_on_left,
+ BOOL minimizable,
+ BOOL close_btn,
+ BOOL bordered)
+: LLPanel(name, rect, bordered)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+}
+
+LLFloater::LLFloater(const LLString& name, const LLString& rect_control, const LLString& title,
+ BOOL resizable,
+ S32 min_width,
+ S32 min_height,
+ BOOL drag_on_left,
+ BOOL minimizable,
+ BOOL close_btn,
+ BOOL bordered)
+: LLPanel(name, rect_control, bordered)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+}
+
+
+// Note: Floaters constructed from XML call init() twice!
+void LLFloater::init(const LLString& title,
+ BOOL resizable, S32 min_width, S32 min_height,
+ BOOL drag_on_left, BOOL minimizable, BOOL close_btn)
+{
+ // Init function can be called more than once, so clear out old data.
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ if (mButtons[i] != NULL)
+ {
+ removeChild(mButtons[i]);
+ delete mButtons[i];
+ mButtons[i] = NULL;
+ }
+ }
+ mButtonScale = 1.f;
+
+ LLPanel::deleteAllChildren();
+ //sjb: HACK! we had a border which was just deleted, so re-create it
+ if (mBorder != NULL)
+ {
+ addBorder();
+ }
+
+ // chrome floaters don't take focus at all
+ mIsFocusRoot = !getIsChrome();
+
+ // Reset cached pointers
+ mDragHandle = NULL;
+ for (S32 i = 0; i < 4; i++)
+ {
+ mResizeBar[i] = NULL;
+ mResizeHandle[i] = NULL;
+ }
+ mCanTearOff = TRUE;
+ mEditing = FALSE;
+
+ // Clicks stop here.
+ setMouseOpaque(TRUE);
+
+ mFirstLook = TRUE;
+ mForeground = FALSE;
+ mDragOnLeft = drag_on_left == TRUE;
+
+ // Floaters always draw their background, unlike every other panel.
+ setBackgroundVisible(TRUE);
+
+ // Floaters start not minimized. When minimized, they save their
+ // prior rectangle to be used on restore.
+ mMinimized = FALSE;
+ mPreviousRect.set(0,0,0,0);
+
+ S32 close_pad; // space to the right of close box
+ S32 close_box_size; // For layout purposes, how big is the close box?
+ if (close_btn)
+ {
+ close_box_size = LLFLOATER_CLOSE_BOX_SIZE;
+ close_pad = 0;
+ }
+ else
+ {
+ close_box_size = 0;
+ close_pad = 0;
+ }
+
+ S32 minimize_box_size;
+ S32 minimize_pad;
+ if (minimizable && !drag_on_left)
+ {
+ minimize_box_size = LLFLOATER_CLOSE_BOX_SIZE;
+ minimize_pad = 0;
+ }
+ else
+ {
+ minimize_box_size = 0;
+ minimize_pad = 0;
+ }
+
+ // Drag Handle
+ // Add first so it's in the background.
+// const S32 drag_pad = 2;
+ LLRect drag_handle_rect;
+ if (!drag_on_left)
+ {
+ drag_handle_rect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+
+ /*
+ drag_handle_rect.setLeftTopAndSize(
+ 0, mRect.getHeight(),
+ mRect.getWidth()
+ - LLPANEL_BORDER_WIDTH
+ - drag_pad
+ - minimize_box_size - minimize_pad
+ - close_box_size - close_pad,
+ DRAG_HANDLE_HEIGHT);
+ */
+ mDragHandle = new LLDragHandleTop( "Drag Handle", drag_handle_rect, title );
+ }
+ else
+ {
+ drag_handle_rect.setOriginAndSize(
+ 0, 0,
+ DRAG_HANDLE_WIDTH,
+ mRect.getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
+ mDragHandle = new LLDragHandleLeft("drag", drag_handle_rect, title );
+ }
+ mDragHandle->setSaveToXML(false);
+ addChild(mDragHandle);
+
+ // Resize Handle
+ mResizable = resizable;
+ mMinWidth = min_width;
+ mMinHeight = min_height;
+
+ if( mResizable )
+ {
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ mResizeBar[0] = new LLResizeBar(
+ "resizebar_left",
+ LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0),
+ min_width, min_height, LLResizeBar::LEFT );
+ mResizeBar[0]->setSaveToXML(false);
+ addChild( mResizeBar[0] );
+
+ mResizeBar[1] = new LLResizeBar(
+ "resizebar_top",
+ LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS),
+ min_width, min_height, LLResizeBar::TOP );
+ mResizeBar[1]->setSaveToXML(false);
+ addChild( mResizeBar[1] );
+
+ mResizeBar[2] = new LLResizeBar(
+ "resizebar_right",
+ LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0),
+ min_width, min_height, LLResizeBar::RIGHT );
+ mResizeBar[2]->setSaveToXML(false);
+ addChild( mResizeBar[2] );
+
+ mResizeBar[3] = new LLResizeBar(
+ "resizebar_bottom",
+ LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0),
+ min_width, min_height, LLResizeBar::BOTTOM );
+ mResizeBar[3]->setSaveToXML(false);
+ addChild( mResizeBar[3] );
+
+
+ // Resize handles (corners)
+ mResizeHandle[0] = new LLResizeHandle(
+ "Resize Handle",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, mRect.getWidth(), 0),
+ min_width,
+ min_height,
+ LLResizeHandle::RIGHT_BOTTOM);
+ mResizeHandle[0]->setSaveToXML(false);
+ addChild(mResizeHandle[0]);
+
+ mResizeHandle[1] = new LLResizeHandle( "resize",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_HANDLE_HEIGHT),
+ min_width,
+ min_height,
+ LLResizeHandle::RIGHT_TOP );
+ mResizeHandle[1]->setSaveToXML(false);
+ addChild(mResizeHandle[1]);
+
+ mResizeHandle[2] = new LLResizeHandle( "resize",
+ LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
+ min_width,
+ min_height,
+ LLResizeHandle::LEFT_BOTTOM );
+ mResizeHandle[2]->setSaveToXML(false);
+ addChild(mResizeHandle[2]);
+
+ mResizeHandle[3] = new LLResizeHandle( "resize",
+ LLRect( 0, mRect.getHeight(), RESIZE_HANDLE_WIDTH, mRect.getHeight() - RESIZE_HANDLE_HEIGHT ),
+ min_width,
+ min_height,
+ LLResizeHandle::LEFT_TOP );
+ mResizeHandle[3]->setSaveToXML(false);
+ addChild(mResizeHandle[3]);
+ }
+ else
+ {
+ mResizeBar[0] = NULL;
+ mResizeBar[1] = NULL;
+ mResizeBar[2] = NULL;
+ mResizeBar[3] = NULL;
+
+ mResizeHandle[0] = NULL;
+ mResizeHandle[1] = NULL;
+ mResizeHandle[2] = NULL;
+ mResizeHandle[3] = NULL;
+ }
+
+ // Close button.
+ if (close_btn)
+ {
+ mButtonsEnabled[BUTTON_CLOSE] = TRUE;
+ }
+
+ // Minimize button only for top draggers
+ if ( !drag_on_left && minimizable )
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ }
+
+ buildButtons();
+
+ // JC - Don't do this here, because many floaters first construct themselves,
+ // then show themselves. Put it in setVisibleAndFrontmost.
+ // make_ui_sound("UISndWindowOpen");
+
+ // RN: floaters are created in the invisible state
+ setVisible(FALSE);
+
+ // add self to handle->floater map
+ sFloaterMap[mViewHandle] = this;
+
+ if (!getParent())
+ {
+ gFloaterView->addChild(this);
+ }
+}
+
+// virtual
+LLFloater::~LLFloater()
+{
+ control_map_t::iterator itor;
+ for (itor = mFloaterControls.begin(); itor != mFloaterControls.end(); ++itor)
+ {
+ delete itor->second;
+ }
+ mFloaterControls.clear();
+
+ //// am I not hosted by another floater?
+ //if (mHostHandle.isDead())
+ //{
+ // LLFloaterView* parent = (LLFloaterView*) getParent();
+
+ // if( parent )
+ // {
+ // parent->removeChild( this );
+ // }
+ //}
+
+ // Just in case we might still have focus here, release it.
+ releaseFocus();
+
+ // This is important so that floaters with persistent rects (i.e., those
+ // created with rect control rather than an LLRect) are restored in their
+ // correct, non-minimized positions.
+ setMinimized( FALSE );
+
+ sFloaterMap.erase(mViewHandle);
+
+ delete mDragHandle;
+ for (S32 i = 0; i < 4; i++)
+ {
+ delete mResizeBar[i];
+ delete mResizeHandle[i];
+ }
+}
+
+// virtual
+EWidgetType LLFloater::getWidgetType() const
+{
+ return WIDGET_TYPE_FLOATER;
+}
+
+// virtual
+LLString LLFloater::getWidgetTag() const
+{
+ return LL_FLOATER_TAG;
+}
+
+void LLFloater::destroy()
+{
+ die();
+}
+
+void LLFloater::setVisible( BOOL visible )
+{
+ LLPanel::setVisible(visible);
+ if( visible && mFirstLook )
+ {
+ mFirstLook = FALSE;
+ }
+
+ if( !visible )
+ {
+ if( gFocusMgr.childIsTopView( this ) )
+ {
+ gFocusMgr.setTopView(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+ }
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+
+ if (floaterp)
+ {
+ floaterp->setVisible(visible);
+ }
+ ++dependent_it;
+ }
+}
+
+LLView* LLFloater::getRootMostFastFrameView()
+{
+ // trying to render a background floater in a fast frame, abort!!!
+ //if (!isFrontmost())
+ //{
+ // gViewerWindow->finishFastFrame();
+ //}
+
+ return LLView::getRootMostFastFrameView();
+}
+
+void LLFloater::open()
+{
+ //RN: for now, we don't allow rehosting from one multifloater to another
+ // just need to fix the bugs
+ LLMultiFloater* hostp = getHost();
+ if (sHostp != NULL && hostp == NULL)
+ {
+ // needs a host
+ sHostp->addFloater(this, TRUE);
+ }
+ else if (hostp != NULL)
+ {
+ // already hosted
+ hostp->showFloater(this);
+ }
+ else
+ {
+ setMinimized(FALSE);
+ setVisibleAndFrontmost(mAutoFocus);
+ }
+
+ if (mSoundFlags != SILENT)
+ {
+ if (!getVisible() || isMinimized())
+ {
+ make_ui_sound("UISndWindowOpen");
+ }
+ }
+}
+
+void LLFloater::close(bool app_quitting)
+{
+ // Always unminimize before trying to close.
+ // Most of the time the user will never see this state.
+ setMinimized(FALSE);
+
+ if (canClose())
+ {
+ if (getHost())
+ {
+ ((LLMultiFloater*)getHost())->removeFloater(this);
+ }
+
+ if (mSoundFlags != SILENT
+ && getVisible()
+ && !app_quitting)
+ {
+ make_ui_sound("UISndWindowClose");
+ }
+
+ // now close dependent floater
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ ++dependent_it;
+ floaterp->close();
+ }
+ else
+ {
+ mDependents.erase(dependent_it++);
+ }
+ }
+
+ cleanupHandles();
+ gFocusMgr.clearLastFocusForGroup(this);
+
+ // Do this early, so UI controls will commit before the
+ // window is taken down.
+ releaseFocus();
+
+ // give focus to dependee floater if it exists, and we had focus first
+ if (isDependent())
+ {
+ LLFloater* dependee = LLFloater::getFloaterByHandle(mDependeeHandle);
+ if (dependee && !dependee->isDead())
+ {
+ dependee->setFocus(TRUE);
+ }
+ }
+
+ // Let floater do cleanup.
+ onClose(app_quitting);
+ }
+}
+
+
+void LLFloater::releaseFocus()
+{
+ if( gFocusMgr.childIsTopView( this ) )
+ {
+ gFocusMgr.setTopView(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasKeyboardFocus( this ) )
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+}
+
+
+void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
+{
+ mMinWidth = min_width;
+ mMinHeight = min_height;
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( mResizeBar[i] )
+ {
+ mResizeBar[i]->setResizeLimits( min_width, min_height );
+ }
+ if( mResizeHandle[i] )
+ {
+ mResizeHandle[i]->setResizeLimits( min_width, min_height );
+ }
+ }
+}
+
+
+void LLFloater::center()
+{
+ if(getHost())
+ {
+ // hosted floaters can't move
+ return;
+ }
+ const LLRect &window = gFloaterView->getRect();
+
+ S32 left = window.mLeft + (window.getWidth() - mRect.getWidth()) / 2;
+ S32 bottom = window.mBottom + (window.getHeight() - mRect.getHeight()) / 2;
+
+ translate( left - mRect.mLeft, bottom - mRect.mBottom );
+}
+
+void LLFloater::applyRectControl()
+{
+ if (!mRectControl.empty())
+ {
+ const LLRect& rect = LLUI::sConfigGroup->getRect(mRectControl);
+ translate( rect.mLeft - mRect.mLeft, rect.mBottom - mRect.mBottom);
+ if (mResizable)
+ {
+ reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+ }
+ }
+}
+
+void LLFloater::setTitle( const LLString& title )
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ mDragHandle->setTitle( title );
+}
+
+const LLString& LLFloater::getTitle() const
+{
+ return mDragHandle ? mDragHandle->getTitle() : LLString::null;
+}
+
+void LLFloater::translate(S32 x, S32 y)
+{
+ LLView::translate(x, y);
+
+ if (x != 0 || y != 0)
+ {
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); ++dependent_it)
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ // is a dependent snapped to us?
+ if (floaterp && floaterp->getSnapTarget() == mViewHandle)
+ {
+ floaterp->translate(x, y);
+ }
+ }
+ }
+}
+
+BOOL LLFloater::canSnapTo(LLView* other_view)
+{
+ if (other_view && other_view != getParent())
+ {
+ LLFloater* other_floaterp = (LLFloater*)other_view;
+
+ if (other_floaterp->getSnapTarget() == mViewHandle && mDependents.find(other_floaterp->getHandle()) != mDependents.end())
+ {
+ // this is a dependent that is already snapped to us, so don't snap back to it
+ return FALSE;
+ }
+ }
+
+ return LLView::canSnapTo(other_view);
+}
+
+void LLFloater::snappedTo(LLView* snap_view)
+{
+ if (!snap_view || snap_view == getParent())
+ {
+ clearSnapTarget();
+ }
+ else
+ {
+ //RN: assume it's a floater as it must be a sibling to our parent floater
+ LLFloater* floaterp = (LLFloater*)snap_view;
+
+ setSnapTarget(floaterp->getHandle());
+ }
+}
+
+void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ S32 old_width = mRect.getWidth();
+ S32 old_height = mRect.getHeight();
+
+ LLView::reshape(width, height, called_from_parent);
+
+ if (width != old_width || height != old_height)
+ {
+ // gather all snapped dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); ++dependent_it)
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ // is a dependent snapped to us?
+ if (floaterp && floaterp->getSnapTarget() == mViewHandle)
+ {
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ // check to see if it snapped to right or top
+ LLRect floater_rect = floaterp->getRect();
+ if (floater_rect.mLeft - mRect.mLeft >= old_width ||
+ floater_rect.mRight == mRect.mLeft + old_width)
+ {
+ // was snapped directly onto right side or aligned with it
+ delta_x += width - old_width;
+ }
+ if (floater_rect.mBottom - mRect.mBottom >= old_height ||
+ floater_rect.mTop == mRect.mBottom + old_height)
+ {
+ // was snapped directly onto top side or aligned with it
+ delta_y += height - old_height;
+ }
+
+ floaterp->translate(delta_x, delta_y);
+ }
+ }
+ }
+}
+
+void LLFloater::setMinimized(BOOL minimize)
+{
+ if (minimize == mMinimized) return;
+
+ if (minimize)
+ {
+ mMinimized = TRUE;
+
+ mPreviousRect = mRect;
+
+ reshape( MINIMIZED_WIDTH, LLFLOATER_HEADER_SIZE, TRUE);
+
+ S32 left, bottom;
+ gFloaterView->getMinimizePosition(&left, &bottom);
+ setOrigin( left, bottom );
+
+ if (mButtonsEnabled[BUTTON_MINIMIZE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = TRUE;
+ }
+
+ mMinimizedHiddenChildren.clear();
+ // hide all children
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (!viewp->getVisible())
+ {
+ mMinimizedHiddenChildren.push_back(viewp);
+ }
+ viewp->setVisible(FALSE);
+ }
+
+ // except the special controls
+ if (mDragHandle)
+ {
+ mDragHandle->setVisible(TRUE);
+ }
+
+ setBorderVisible(TRUE);
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ floaterp->setVisible(FALSE);
+ }
+ ++dependent_it;
+ }
+
+ // Lose keyboard focus when minimized
+ releaseFocus();
+ }
+ else
+ {
+ reshape( mPreviousRect.getWidth(), mPreviousRect.getHeight(), TRUE );
+ setOrigin( mPreviousRect.mLeft, mPreviousRect.mBottom );
+
+ mMinimized = FALSE;
+
+ if (mButtonsEnabled[BUTTON_RESTORE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+
+ // show all children
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ viewp->setVisible(TRUE);
+ }
+
+ std::vector<LLView*>::iterator itor = mMinimizedHiddenChildren.begin();
+ while (itor != mMinimizedHiddenChildren.end())
+ {
+ (*itor)->setVisible(FALSE);
+ ++itor;
+ }
+ mMinimizedHiddenChildren.clear();
+
+ // show dependent floater
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ floaterp->setVisible(TRUE);
+ }
+ ++dependent_it;
+ }
+ }
+ make_ui_sound("UISndWindowClose");
+ updateButtons();
+}
+
+void LLFloater::setFocus( BOOL b )
+{
+ if (b && getIsChrome())
+ {
+ return;
+ }
+ LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
+ // a descendent already has focus
+ BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this);
+
+ // give focus to first valid descendent
+ LLPanel::setFocus(b);
+
+ if (b)
+ {
+ // only push focused floaters to front of stack if not in midst of ctrl-tab cycle
+ if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode())
+ {
+ if (!isFrontmost())
+ {
+ setFrontmost();
+ }
+ }
+
+ // when getting focus, delegate to last descendent which had focus
+ if (last_focus && !child_had_focus &&
+ last_focus->isInEnabledChain() &&
+ last_focus->isInVisibleChain())
+ {
+ // FIXME: should handle case where focus doesn't stick
+ last_focus->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloater::setIsChrome(BOOL is_chrome)
+{
+ // chrome floaters don't take focus at all
+ if (is_chrome)
+ {
+ // remove focus if we're changing to chrome
+ setFocus(FALSE);
+ // can't Ctrl-Tab to "chrome" floaters
+ mIsFocusRoot = FALSE;
+ }
+
+ // no titles displayed on "chrome" floaters
+ mDragHandle->setTitleVisible(!is_chrome);
+
+ LLPanel::setIsChrome(is_chrome);
+}
+
+// Change the draw style to account for the foreground state.
+void LLFloater::setForeground(BOOL front)
+{
+ if (front != mForeground)
+ {
+ mForeground = front;
+ mDragHandle->setForeground( front );
+
+ if (!front)
+ {
+ releaseFocus();
+ }
+
+ setBackgroundOpaque( front );
+ }
+}
+
+void LLFloater::cleanupHandles()
+{
+ // remove handles to non-existent dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (!floaterp)
+ {
+ mDependents.erase(dependent_it++);
+ }
+ else
+ {
+ ++dependent_it;
+ }
+ }
+}
+
+void LLFloater::setHost(LLMultiFloater* host)
+{
+ if (mHostHandle.isDead() && host)
+ {
+ // make buttons smaller for hosted windows to differentiate from parent
+ mButtonScale = 0.9f;
+
+ // add tear off button
+ if (mCanTearOff)
+ {
+ mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE;
+ }
+
+ mIsFocusRoot = FALSE;
+ }
+ else if (!mHostHandle.isDead() && !host)
+ {
+ mButtonScale = 1.f;
+ mIsFocusRoot = TRUE;
+ //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
+ }
+ updateButtons();
+ if (host)
+ {
+ mHostHandle = host->getHandle();
+ mLastHostHandle = host->getHandle();
+ }
+ else
+ {
+ mHostHandle.markDead();
+ }
+}
+
+void LLFloater::moveResizeHandleToFront()
+{
+ // 0 is the bottom right
+ if( mResizeHandle[0] )
+ {
+ sendChildToFront(mResizeHandle[0]);
+ }
+}
+
+BOOL LLFloater::isFrontmost()
+{
+ return gFloaterView && gFloaterView->getFrontmost() == this && getVisible();
+}
+
+void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
+{
+ mDependents.insert(floaterp->getHandle());
+ floaterp->mDependeeHandle = getHandle();
+
+ if (reposition)
+ {
+ floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
+ floaterp->setSnapTarget(mViewHandle);
+ }
+ gFloaterView->adjustToFitScreen(floaterp, FALSE);
+ if (floaterp->isFrontmost())
+ {
+ // make sure to bring self and sibling floaters to front
+ gFloaterView->bringToFront(floaterp);
+ }
+}
+
+void LLFloater::addDependentFloater(LLViewHandle dependent, BOOL reposition)
+{
+ LLFloater* dependent_floaterp = LLFloater::getFloaterByHandle(dependent);
+ if(dependent_floaterp)
+ {
+ addDependentFloater(dependent_floaterp, reposition);
+ }
+}
+
+void LLFloater::removeDependentFloater(LLFloater* floaterp)
+{
+ mDependents.erase(floaterp->getHandle());
+ floaterp->mDependeeHandle = LLViewHandle::sDeadHandle;
+}
+
+// virtual
+BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if( mMinimized )
+ {
+ // Offer the click to the close button.
+ // Any other click = restore
+ if( mButtonsEnabled[BUTTON_CLOSE] )
+ {
+ S32 local_x = x - mButtons[BUTTON_CLOSE]->getRect().mLeft;
+ S32 local_y = y - mButtons[BUTTON_CLOSE]->getRect().mBottom;
+
+ if (mButtons[BUTTON_CLOSE]->pointInView(local_x, local_y)
+ && mButtons[BUTTON_CLOSE]->handleMouseDown(local_x, local_y, mask))
+ {
+ // close button handled it, return
+ return TRUE;
+ }
+ }
+
+ // restore
+ bringToFront( x, y );
+ return TRUE;
+ }
+ else
+ {
+ bringToFront( x, y );
+ return LLPanel::handleMouseDown( x, y, mask );
+ }
+}
+
+// virtual
+BOOL LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL was_minimized = mMinimized;
+ bringToFront( x, y );
+ return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
+}
+
+
+// virtual
+BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL was_minimized = mMinimized;
+ setMinimized(FALSE);
+ return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
+}
+
+void LLFloater::bringToFront( S32 x, S32 y )
+{
+ if (getVisible() && pointInView(x, y))
+ {
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->showFloater(this);
+ }
+ else
+ {
+ LLFloaterView* parent = (LLFloaterView*) getParent();
+ if (parent)
+ {
+ parent->bringToFront( this );
+ }
+ }
+ }
+}
+
+
+// virtual
+void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
+{
+ setVisible(TRUE);
+ setFrontmost(take_focus);
+}
+
+void LLFloater::setFrontmost(BOOL take_focus)
+{
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ // this will bring the host floater to the front and select
+ // the appropriate panel
+ hostp->showFloater(this);
+ }
+ else
+ {
+ // there are more than one floater view
+ // so we need to query our parent directly
+ ((LLFloaterView*)getParent())->bringToFront(this, take_focus);
+ }
+}
+
+// static
+LLFloater* LLFloater::getFloaterByHandle(LLViewHandle handle)
+{
+ LLFloater* floater = NULL;
+ if (sFloaterMap.count(handle))
+ {
+ floater = sFloaterMap[handle];
+ }
+ if (floater && !floater->isDead())
+ {
+ return floater;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//static
+void LLFloater::setEditModeEnabled(BOOL enable)
+{
+ if (enable != sEditModeEnabled)
+ {
+ S32 count = 0;
+ std::map<LLViewHandle, LLFloater*>::iterator iter;
+ for(iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
+ {
+ LLFloater* floater = iter->second;
+ if (!floater->isDead())
+ {
+ iter->second->mButtonsEnabled[BUTTON_EDIT] = enable;
+ iter->second->updateButtons();
+ }
+ count++;
+ }
+ }
+
+ sEditModeEnabled = enable;
+}
+
+//static
+BOOL LLFloater::getEditModeEnabled()
+{
+ return sEditModeEnabled;
+}
+
+// static
+void LLFloater::onClickMinimize(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->setMinimized( !self->isMinimized() );
+}
+
+void LLFloater::onClickTearOff(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ LLMultiFloater* host_floater = self->getHost();
+ if (host_floater) //Tear off
+ {
+ LLRect new_rect;
+ host_floater->removeFloater(self);
+ // reparent to floater view
+ gFloaterView->addChild(self);
+
+ new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - LLFLOATER_HEADER_SIZE - 5, self->mRect.getWidth(), self->mRect.getHeight());
+
+ self->open();
+ self->setRect(new_rect);
+ gFloaterView->adjustToFitScreen(self, FALSE);
+ self->setCanDrag(TRUE);
+ self->setCanResize(TRUE);
+ self->setCanMinimize(TRUE);
+ }
+ else //Attach to parent.
+ {
+ LLMultiFloater* new_host = (LLMultiFloater*)LLFloater::getFloaterByHandle(self->mLastHostHandle);
+ if (new_host)
+ {
+ new_host->showFloater(self);
+ }
+ }
+}
+
+// static
+void LLFloater::onClickEdit(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->mEditing = self->mEditing ? FALSE : TRUE;
+}
+
+// static
+void LLFloater::closeByMenu( void* userdata )
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self || self->getHost()) return;
+
+ LLFloaterView* parent = (LLFloaterView*) self->getParent();
+
+ // grab focus status before close just in case floater is deleted
+ BOOL has_focus = gFocusMgr.childHasKeyboardFocus(self);
+ self->close();
+
+ // if this floater used to have focus and now nothing took focus
+ // give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
+ if (has_focus && gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ parent->focusFrontFloater();
+ }
+
+}
+
+
+// static
+void LLFloater::onClickClose( void* userdata )
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->close();
+}
+
+
+// virtual
+void LLFloater::draw()
+{
+ if( getVisible() )
+ {
+ // draw background
+ if( mBgVisible )
+ {
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = mRect.getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = mRect.getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow");
+ F32 shadow_offset = (F32)LLUI::sConfigGroup->getS32("DropShadowFloater");
+ if (!mBgOpaque)
+ {
+ shadow_offset *= 0.2f;
+ shadow_color.mV[VALPHA] *= 0.5f;
+ }
+ gl_drop_shadow(left, top, right, bottom,
+ shadow_color,
+ llround(shadow_offset));
+
+ // No transparent windows in simple UI
+ if (mBgOpaque)
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
+ }
+ else
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
+ }
+
+ if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getTitle().empty())
+ {
+ // draw highlight on title bar to indicate focus. RDW
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ LLRect r = getRect();
+ gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
+ LLUI::sColorsGroup->getColor("TitleBarFocusColor"), 0, TRUE);
+ }
+ }
+
+ if( mDefaultBtn)
+ {
+ if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ // is this button a direct descendent and not a nested widget (e.g. checkbox)?
+ BOOL focus_is_child_button = focus_ctrl->getWidgetType() == WIDGET_TYPE_BUTTON && focus_ctrl->getParent() == this;
+ // only enable default button when current focus is not a button
+ mDefaultBtn->setBorderEnabled(!focus_is_child_button);
+ }
+ else
+ {
+ mDefaultBtn->setBorderEnabled(FALSE);
+ }
+ }
+
+ // draw children
+ LLView* focused_child = gFocusMgr.getKeyboardFocus();
+ BOOL focused_child_visible = FALSE;
+ if (focused_child && focused_child->getParent() == this)
+ {
+ focused_child_visible = focused_child->getVisible();
+ focused_child->setVisible(FALSE);
+ }
+
+ LLView::draw();
+
+ if( mBgVisible )
+ {
+ // add in a border to improve spacialized visual aclarity ;)
+ // use lines instead of gl_rect_2d so we can round the edges as per james' recommendation
+ LLUI::setLineWidth(1.5f);
+ LLColor4 outlineColor = gFocusMgr.childHasKeyboardFocus(this) ? LLUI::sColorsGroup->getColor("FloaterFocusBorderColor") : LLUI::sColorsGroup->getColor("FloaterUnfocusBorderColor");
+ gl_rect_2d_offset_local(0, mRect.getHeight() + 1, mRect.getWidth() + 1, 0, outlineColor, -LLPANEL_BORDER_WIDTH, FALSE);
+ LLUI::setLineWidth(1.f);
+ }
+
+ if (focused_child_visible)
+ {
+ focused_child->setVisible(TRUE);
+ }
+ drawChild(focused_child);
+ }
+}
+
+// virtual
+void LLFloater::onClose(bool app_quitting)
+{
+ destroy();
+}
+
+// virtual
+BOOL LLFloater::canClose()
+{
+ return TRUE;
+}
+
+// virtual
+BOOL LLFloater::canSaveAs()
+{
+ return FALSE;
+}
+
+// virtual
+void LLFloater::saveAs()
+{
+}
+
+void LLFloater::setCanMinimize(BOOL can_minimize)
+{
+ // removing minimize/restore button programmatically,
+ // go ahead and uniminimize floater
+ if (!can_minimize)
+ {
+ setMinimized(FALSE);
+ }
+
+ if (can_minimize)
+ {
+ if (isMinimized())
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = TRUE;
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+
+ updateButtons();
+}
+
+void LLFloater::setCanClose(BOOL can_close)
+{
+ mButtonsEnabled[BUTTON_CLOSE] = can_close;
+
+ updateButtons();
+}
+
+void LLFloater::setCanTearOff(BOOL can_tear_off)
+{
+ mCanTearOff = can_tear_off;
+ mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
+
+ updateButtons();
+}
+
+
+void LLFloater::setCanResize(BOOL can_resize)
+{
+ if (mResizable && !can_resize)
+ {
+ removeChild(mResizeBar[0]);
+ removeChild(mResizeBar[1]);
+ removeChild(mResizeBar[2]);
+ removeChild(mResizeBar[3]);
+ removeChild(mResizeHandle[0]);
+ removeChild(mResizeHandle[1]);
+ removeChild(mResizeHandle[2]);
+ removeChild(mResizeHandle[3]);
+ delete mResizeBar[0];
+ delete mResizeBar[1];
+ delete mResizeBar[2];
+ delete mResizeBar[3];
+ delete mResizeHandle[0];
+ delete mResizeHandle[1];
+ delete mResizeHandle[2];
+ mResizeHandle[3] = NULL;
+ mResizeBar[0] = NULL;
+ mResizeBar[1] = NULL;
+ mResizeBar[2] = NULL;
+ mResizeBar[3] = NULL;
+ mResizeHandle[0] = NULL;
+ mResizeHandle[1] = NULL;
+ mResizeHandle[2] = NULL;
+ mResizeHandle[3] = NULL;
+ }
+ else if (!mResizable && can_resize)
+ {
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ mResizeBar[0] = new LLResizeBar(
+ "resizebar_left",
+ LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0),
+ mMinWidth, mMinHeight, LLResizeBar::LEFT );
+ mResizeBar[0]->setSaveToXML(false);
+ addChild( mResizeBar[0] );
+
+ mResizeBar[1] = new LLResizeBar(
+ "resizebar_top",
+ LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS),
+ mMinWidth, mMinHeight, LLResizeBar::TOP );
+ mResizeBar[1]->setSaveToXML(false);
+ addChild( mResizeBar[1] );
+
+ mResizeBar[2] = new LLResizeBar(
+ "resizebar_right",
+ LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0),
+ mMinWidth, mMinHeight, LLResizeBar::RIGHT );
+ mResizeBar[2]->setSaveToXML(false);
+ addChild( mResizeBar[2] );
+
+ mResizeBar[3] = new LLResizeBar(
+ "resizebar_bottom",
+ LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0),
+ mMinWidth, mMinHeight, LLResizeBar::BOTTOM );
+ mResizeBar[3]->setSaveToXML(false);
+ addChild( mResizeBar[3] );
+
+
+ // Resize handles (corners)
+ mResizeHandle[0] = new LLResizeHandle(
+ "Resize Handle",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, mRect.getWidth(), 0),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::RIGHT_BOTTOM);
+ mResizeHandle[0]->setSaveToXML(false);
+ addChild(mResizeHandle[0]);
+
+ mResizeHandle[1] = new LLResizeHandle( "resize",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_HANDLE_HEIGHT),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::RIGHT_TOP );
+ mResizeHandle[1]->setSaveToXML(false);
+ addChild(mResizeHandle[1]);
+
+ mResizeHandle[2] = new LLResizeHandle( "resize",
+ LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::LEFT_BOTTOM );
+ mResizeHandle[2]->setSaveToXML(false);
+ addChild(mResizeHandle[2]);
+
+ mResizeHandle[3] = new LLResizeHandle( "resize",
+ LLRect( 0, mRect.getHeight(), RESIZE_HANDLE_WIDTH, mRect.getHeight() - RESIZE_HANDLE_HEIGHT ),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::LEFT_TOP );
+ mResizeHandle[3]->setSaveToXML(false);
+ addChild(mResizeHandle[3]);
+ }
+ mResizable = can_resize;
+}
+
+void LLFloater::setCanDrag(BOOL can_drag)
+{
+ // if we delete drag handle, we no longer have access to the floater's title
+ // so just enable/disable it
+ if (!can_drag && mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(FALSE);
+ }
+ else if (can_drag && !mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(TRUE);
+ }
+}
+
+void LLFloater::updateButtons()
+{
+ S32 button_count = 0;
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ if (mButtonsEnabled[i])
+ {
+ button_count++;
+
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count,
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+ else
+ {
+ btn_rect.setLeftTopAndSize(
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count,
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP,
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+
+ mButtons[i]->setRect(btn_rect);
+ mButtons[i]->setVisible(TRUE);
+ mButtons[i]->setEnabled(TRUE);
+ // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
+ mButtons[i]->setTabStop(i == BUTTON_RESTORE);
+ }
+ else if (mButtons[i])
+ {
+ mButtons[i]->setVisible(FALSE);
+ mButtons[i]->setEnabled(FALSE);
+ }
+ }
+
+ mDragHandle->setMaxTitleWidth(mRect.getWidth() - (button_count * (LLFLOATER_CLOSE_BOX_SIZE + 1)));
+}
+
+void LLFloater::buildButtons()
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+ else
+ {
+ btn_rect.setLeftTopAndSize(
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP,
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+
+ LLButton* buttonp = new LLButton(
+ sButtonNames[i],
+ btn_rect,
+ sButtonActiveImageNames[i],
+ sButtonPressedImageNames[i],
+ "",
+ sButtonCallbacks[i],
+ this,
+ LLFontGL::sSansSerif);
+
+ buttonp->setTabStop(FALSE);
+ buttonp->setFollowsTop();
+ buttonp->setFollowsRight();
+ buttonp->setToolTip( sButtonToolTips[i] );
+ buttonp->setImageColor(LLUI::sColorsGroup->getColor("FloaterButtonImageColor"));
+ buttonp->setHoverImages(sButtonPressedImageNames[i],
+ sButtonPressedImageNames[i]);
+ buttonp->setScaleImage(TRUE);
+ buttonp->setSaveToXML(false);
+ addChild(buttonp);
+ mButtons[i] = buttonp;
+ }
+
+ updateButtons();
+}
+
+/////////////////////////////////////////////////////
+// LLFloaterView
+
+LLFloaterView::LLFloaterView( const LLString& name, const LLRect& rect )
+: LLUICtrl( name, rect, FALSE, NULL, NULL, FOLLOWS_ALL ),
+ mFocusCycleMode(FALSE),
+ mSnapOffsetBottom(0)
+{
+ setTabStop(FALSE);
+ resetStartingFloaterPosition();
+}
+
+EWidgetType LLFloaterView::getWidgetType() const
+{
+ return WIDGET_TYPE_FLOATER_VIEW;
+}
+
+LLString LLFloaterView::getWidgetTag() const
+{
+ return LL_FLOATER_VIEW_TAG;
+}
+
+// By default, adjust vertical.
+void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ reshape(width, height, called_from_parent, ADJUST_VERTICAL_YES);
+}
+
+// When reshaping this view, make the floaters follow their closest edge.
+void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical)
+{
+ S32 old_width = mRect.getWidth();
+ S32 old_height = mRect.getHeight();
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater* floaterp = (LLFloater*)viewp;
+ if (floaterp->isDependent())
+ {
+ // dependents use same follow flags as their "dependee"
+ continue;
+ }
+ LLRect r = floaterp->getRect();
+
+ // Compute absolute distance from each edge of screen
+ S32 left_offset = llabs(r.mLeft - 0);
+ S32 right_offset = llabs(old_width - r.mRight);
+
+ S32 top_offset = llabs(old_height - r.mTop);
+ S32 bottom_offset = llabs(r.mBottom - 0);
+
+ // Make if follow the edge it is closest to
+ U32 follow_flags = 0x0;
+
+ if (left_offset < right_offset)
+ {
+ follow_flags |= FOLLOWS_LEFT;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_RIGHT;
+ }
+
+ // "No vertical adjustment" usually means that the bottom of the view
+ // has been pushed up or down. Hence we want the floaters to follow
+ // the top.
+ if (!adjust_vertical)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else if (top_offset < bottom_offset)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_BOTTOM;
+ }
+
+ floaterp->setFollows(follow_flags);
+
+ //RN: all dependent floaters copy follow behavior of "parent"
+ for(LLFloater::handle_set_iter_t dependent_it = floaterp->mDependents.begin();
+ dependent_it != floaterp->mDependents.end(); ++dependent_it)
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp)
+ {
+ dependent_floaterp->setFollows(follow_flags);
+ }
+ }
+ }
+
+ LLView::reshape(width, height, called_from_parent);
+}
+
+
+void LLFloaterView::restoreAll()
+{
+ // make sure all subwindows aren't minimized
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ floaterp->setMinimized(FALSE);
+ }
+
+ //FIXME: make sure dependents are restored
+
+ // children then deleted by default view constructor
+}
+
+
+void LLFloaterView::getNewFloaterPosition(S32* left,S32* top)
+{
+ // Workaround: mRect may change between when this object is created and the first time it is used.
+ static BOOL first = TRUE;
+ if( first )
+ {
+ resetStartingFloaterPosition();
+ first = FALSE;
+ }
+
+ const S32 FLOATER_PAD = 16;
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+ LLRect full_window(0, window_size.mY, window_size.mX, 0);
+ LLRect floater_creation_rect(
+ 160,
+ full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
+ full_window.getWidth() * 2 / 3,
+ 130 );
+ floater_creation_rect.stretch( -FLOATER_PAD );
+
+ *left = mNextLeft;
+ *top = mNextTop;
+
+ const S32 STEP = 25;
+ S32 bottom = floater_creation_rect.mBottom + 2 * STEP;
+ S32 right = floater_creation_rect.mRight - 4 * STEP;
+
+ mNextTop -= STEP;
+ mNextLeft += STEP;
+
+ if( (mNextTop < bottom ) || (mNextLeft > right) )
+ {
+ mColumn++;
+ mNextTop = floater_creation_rect.mTop;
+ mNextLeft = STEP * mColumn;
+
+ if( (mNextTop < bottom) || (mNextLeft > right) )
+ {
+ // Advancing the column didn't work, so start back at the beginning
+ resetStartingFloaterPosition();
+ }
+ }
+}
+
+void LLFloaterView::resetStartingFloaterPosition()
+{
+ const S32 FLOATER_PAD = 16;
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+ LLRect full_window(0, window_size.mY, window_size.mX, 0);
+ LLRect floater_creation_rect(
+ 160,
+ full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
+ full_window.getWidth() * 2 / 3,
+ 130 );
+ floater_creation_rect.stretch( -FLOATER_PAD );
+
+ mNextLeft = floater_creation_rect.mLeft;
+ mNextTop = floater_creation_rect.mTop;
+ mColumn = 0;
+}
+
+LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
+{
+ LLRect base_rect = reference_floater->getRect();
+ S32 width = neighbor->getRect().getWidth();
+ S32 height = neighbor->getRect().getHeight();
+ LLRect new_rect = neighbor->getRect();
+
+ LLRect expanded_base_rect = base_rect;
+ expanded_base_rect.stretch(10);
+ for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
+ dependent_it != reference_floater->mDependents.end(); ++dependent_it)
+ {
+ LLFloater* sibling = LLFloater::getFloaterByHandle(*dependent_it);
+ // check for dependents within 10 pixels of base floater
+ if (sibling &&
+ sibling != neighbor &&
+ sibling->getVisible() &&
+ expanded_base_rect.rectInRect(&sibling->getRect()))
+ {
+ base_rect |= sibling->getRect();
+ }
+ }
+
+ S32 left_margin = llmax(0, base_rect.mLeft);
+ S32 right_margin = llmax(0, mRect.getWidth() - base_rect.mRight);
+ S32 top_margin = llmax(0, mRect.getHeight() - base_rect.mTop);
+ S32 bottom_margin = llmax(0, base_rect.mBottom);
+
+ // find position for floater in following order
+ // right->left->bottom->top
+ for (S32 i = 0; i < 5; i++)
+ {
+ if (right_margin > width)
+ {
+ new_rect.translate(base_rect.mRight - neighbor->mRect.mLeft, base_rect.mTop - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (left_margin > width)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mRight, base_rect.mTop - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (bottom_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mLeft, base_rect.mBottom - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (top_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mLeft, base_rect.mTop - neighbor->mRect.mBottom);
+ return new_rect;
+ }
+
+ // keep growing margins to find "best" fit
+ left_margin += 20;
+ right_margin += 20;
+ top_margin += 20;
+ bottom_margin += 20;
+ }
+
+ // didn't find anything, return initial rect
+ return new_rect;
+}
+
+void LLFloaterView::setCycleMode(BOOL mode)
+{
+ mFocusCycleMode = mode;
+}
+
+BOOL LLFloaterView::getCycleMode()
+{
+ return mFocusCycleMode;
+}
+
+void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
+{
+ //FIXME: make this respect floater's mAutoFocus value, instead of using parameter
+ if (child->getHost())
+ {
+ // this floater is hosted elsewhere and hence not one of our children, abort
+ return;
+ }
+ std::vector<LLView*> floaters_to_move;
+ // Look at all floaters...tab
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater *floater = (LLFloater *)viewp;
+
+ // ...but if I'm a dependent floater...
+ if (child->isDependent())
+ {
+ // ...look for floaters that have me as a dependent...
+ LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle());
+
+ if (found_dependent != floater->mDependents.end())
+ {
+ // ...and make sure all children of that floater (including me) are brought to front...
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); )
+ {
+ LLFloater* sibling = LLFloater::getFloaterByHandle(*dependent_it);
+ if (sibling)
+ {
+ floaters_to_move.push_back(sibling);
+ }
+ ++dependent_it;
+ }
+ //...before bringing my parent to the front...
+ floaters_to_move.push_back(floater);
+ }
+ }
+ }
+
+ std::vector<LLView*>::iterator view_it;
+ for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
+ {
+ LLFloater* floaterp = (LLFloater*)(*view_it);
+ sendChildToFront(floaterp);
+
+ floaterp->setMinimized(FALSE);
+ }
+ floaters_to_move.clear();
+
+ // ...then bringing my own dependents to the front...
+ for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
+ dependent_it != child->mDependents.end(); )
+ {
+ LLFloater* dependent = getFloaterByHandle(*dependent_it);
+ if (dependent)
+ {
+ sendChildToFront(dependent);
+ dependent->setMinimized(FALSE);
+ }
+ ++dependent_it;
+ }
+
+ // ...and finally bringing myself to front
+ // (do this last, so that I'm left in front at end of this call)
+ if( *getChildList()->begin() != child )
+ {
+ sendChildToFront(child);
+ }
+ child->setMinimized(FALSE);
+ if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(TRUE);
+ }
+}
+
+void LLFloaterView::highlightFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ // skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
+ if (floater->isDependent())
+ {
+ continue;
+ }
+
+ BOOL floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end();
+ ++dependent_it)
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
+ {
+ floater_or_dependent_has_focus = TRUE;
+ }
+ }
+
+ // now set this floater and all its dependents
+ floater->setForeground(floater_or_dependent_has_focus);
+
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); )
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp)
+ {
+ dependent_floaterp->setForeground(floater_or_dependent_has_focus);
+ }
+ ++dependent_it;
+ }
+
+ floater->cleanupHandles();
+ }
+}
+
+void LLFloaterView::unhighlightFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ floater->setForeground(FALSE);
+ }
+}
+
+void LLFloaterView::focusFrontFloater()
+{
+ LLFloater* floaterp = getFrontmost();
+ if (floaterp)
+ {
+ floaterp->setFocus(TRUE);
+ }
+}
+
+void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
+{
+ // count the number of minimized children
+ S32 count = 0;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater *floater = (LLFloater *)viewp;
+ if (floater->isMinimized())
+ {
+ count++;
+ }
+ }
+
+ // space over for that many and up if necessary
+ S32 tiles_per_row = mRect.getWidth() / MINIMIZED_WIDTH;
+
+ *left = (count % tiles_per_row) * MINIMIZED_WIDTH;
+ *bottom = (count / tiles_per_row) * LLFLOATER_HEADER_SIZE;
+}
+
+
+void LLFloaterView::destroyAllChildren()
+{
+ LLView::deleteAllChildren();
+}
+
+void LLFloaterView::closeAllChildren(bool app_quitting)
+{
+ // iterate over a copy of the list, because closing windows will destroy
+ // some windows on the list.
+ child_list_t child_list = *(getChildList());
+
+ for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
+ {
+ LLView* viewp = *it;
+ child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
+ if (exists == getChildList()->end())
+ {
+ // this floater has already been removed
+ continue;
+ }
+
+ LLFloater* floaterp = (LLFloater*)viewp;
+
+ // Attempt to close floater. This will cause the "do you want to save"
+ // dialogs to appear.
+ if (floaterp->canClose())
+ {
+ floaterp->close(app_quitting);
+ }
+ }
+}
+
+
+BOOL LLFloaterView::allChildrenClosed()
+{
+ // see if there are any visible floaters (some floaters "close"
+ // by setting themselves invisible)
+ S32 visible_count = 0;
+ for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
+ {
+ LLView* viewp = *it;
+ LLFloater* floaterp = (LLFloater*)viewp;
+
+ if (floaterp->getVisible() && floaterp->canClose())
+ {
+ visible_count++;
+ }
+ }
+
+ return (visible_count == 0);
+}
+
+
+void LLFloaterView::refresh()
+{
+ // Constrain children to be entirely on the screen
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ if( floaterp->getVisible() )
+ {
+ adjustToFitScreen(floaterp, TRUE);
+ }
+ }
+}
+
+void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside)
+{
+ if (floater->getParent() != this)
+ {
+ // floater is hosted elsewhere, so ignore
+ return;
+ }
+ S32 screen_width = getSnapRect().getWidth();
+ S32 screen_height = getSnapRect().getHeight();
+ // convert to local coordinate frame
+ LLRect snap_rect_local = getSnapRect();
+ snap_rect_local.translate(-mRect.mLeft, -mRect.mBottom);
+
+ if( floater->isResizable() )
+ {
+ LLRect view_rect = floater->getRect();
+ S32 view_width = view_rect.getWidth();
+ S32 view_height = view_rect.getHeight();
+ S32 min_width;
+ S32 min_height;
+ floater->getResizeLimits( &min_width, &min_height );
+
+ S32 new_width = llmax( min_width, view_width );
+ S32 new_height = llmax( min_height, view_height );
+
+ if( (new_width > screen_width) || (new_height > screen_height) )
+ {
+ new_width = llmin(new_width, screen_width);
+ new_height = llmin(new_height, screen_height);
+
+ floater->reshape( new_width, new_height, TRUE );
+
+ // Make sure the damn thing is actually onscreen.
+ if (floater->translateIntoRect(snap_rect_local, FALSE))
+ {
+ floater->clearSnapTarget();
+ }
+ }
+ else if (!floater->isMinimized())
+ {
+ floater->reshape(new_width, new_height, TRUE);
+ }
+ }
+
+ if (floater->translateIntoRect( snap_rect_local, allow_partial_outside ))
+ {
+ floater->clearSnapTarget();
+ }
+}
+
+void LLFloaterView::draw()
+{
+ if( getVisible() )
+ {
+ refresh();
+
+ // hide focused floater if in cycle mode, so that it can be drawn on top
+ LLFloater* focused_floater = getFocusedFloater();
+ BOOL floater_visible = FALSE;
+ if (mFocusCycleMode && focused_floater)
+ {
+ floater_visible = focused_floater->getVisible();
+ focused_floater->setVisible(FALSE);
+ }
+
+ // And actually do the draw
+ LLView::draw();
+
+ // manually draw focused floater on top when in cycle mode
+ if (mFocusCycleMode && focused_floater)
+ {
+ // draw focused item on top for better feedback
+ focused_floater->setVisible(floater_visible);
+ if (floater_visible)
+ {
+ drawChild(focused_floater);
+ }
+ }
+ }
+}
+
+const LLRect LLFloaterView::getSnapRect() const
+{
+ LLRect snap_rect = mRect;
+ snap_rect.mBottom += mSnapOffsetBottom;
+
+ return snap_rect;
+}
+
+LLFloater *LLFloaterView::getFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLUICtrl* ctrlp = (*child_it)->isCtrl() ? static_cast<LLUICtrl*>(*child_it) : NULL;
+ if ( ctrlp && ctrlp->hasFocus() )
+ {
+ return static_cast<LLFloater *>(ctrlp);
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getFrontmost()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() )
+ {
+ return (LLFloater *)viewp;
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getBackmost()
+{
+ LLFloater* back_most = NULL;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() )
+ {
+ back_most = (LLFloater *)viewp;
+ }
+ }
+ return back_most;
+}
+
+void LLFloaterView::syncFloaterTabOrder()
+{
+ // bring focused floater to front
+ for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ if (gFocusMgr.childHasKeyboardFocus(floaterp))
+ {
+ bringToFront(floaterp, FALSE);
+ break;
+ }
+ }
+
+ // then sync draw order to tab order
+ for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ moveChildToFrontOfTabGroup(floaterp);
+ }
+}
+
+LLFloater* LLFloaterView::getFloaterByHandle(LLViewHandle handle)
+{
+ if (handle == LLViewHandle::sDeadHandle)
+ {
+ return NULL;
+ }
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (((LLFloater*)viewp)->getHandle() == handle)
+ {
+ return (LLFloater*)viewp;
+ }
+ }
+ return NULL;
+}
+
+LLFloater* LLFloaterView::getParentFloater(LLView* viewp)
+{
+ LLView* parentp = viewp->getParent();
+
+ while(parentp && parentp != this)
+ {
+ viewp = parentp;
+ parentp = parentp->getParent();
+ }
+
+ if (parentp == this)
+ {
+ return (LLFloater*)viewp;
+ }
+
+ return NULL;
+}
+
+S32 LLFloaterView::getZOrder(LLFloater* child)
+{
+ S32 rv = 0;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if(viewp == child)
+ {
+ break;
+ }
+ ++rv;
+ }
+ return rv;
+}
+
+void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->pushVisible(visible);
+ }
+ }
+}
+
+void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->popVisible();
+ }
+ }
+}
+
+//
+// LLMultiFloater
+//
+
+LLMultiFloater::LLMultiFloater() :
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(FALSE)
+{
+
+}
+
+LLMultiFloater::LLMultiFloater(LLTabContainerCommon::TabPosition tab_pos) :
+ mTabContainer(NULL),
+ mTabPos(tab_pos),
+ mAutoResize(FALSE)
+{
+
+}
+
+LLMultiFloater::LLMultiFloater(const LLString &name) :
+ LLFloater(name),
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(FALSE)
+{
+}
+
+LLMultiFloater::LLMultiFloater(
+ const LLString& name,
+ const LLRect& rect,
+ LLTabContainer::TabPosition tab_pos,
+ BOOL auto_resize) :
+ LLFloater(name, rect, name),
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(auto_resize)
+{
+ mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);
+}
+
+LLMultiFloater::LLMultiFloater(
+ const LLString& name,
+ const LLString& rect_control,
+ LLTabContainer::TabPosition tab_pos,
+ BOOL auto_resize) :
+ LLFloater(name, rect_control, name),
+ mTabContainer(NULL),
+ mTabPos(tab_pos),
+ mAutoResize(auto_resize)
+{
+ mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable && mTabPos == LLTabContainerCommon::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);
+
+}
+
+LLMultiFloater::~LLMultiFloater()
+{
+}
+
+// virtual
+EWidgetType LLMultiFloater::getWidgetType() const
+{
+ return WIDGET_TYPE_MULTI_FLOATER;
+}
+
+// virtual
+LLString LLMultiFloater::getWidgetTag() const
+{
+ return LL_MULTI_FLOATER_TAG;
+}
+
+void LLMultiFloater::init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn)
+{
+ LLFloater::init(title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+
+ /*mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable && mTabPos == LLTabContainerCommon::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);*/
+}
+
+void LLMultiFloater::open()
+{
+ if (mTabContainer->getTabCount() > 0)
+ {
+ LLFloater::open();
+ }
+ else
+ {
+ // for now, don't allow multifloaters
+ // without any child floaters
+ close();
+ }
+}
+
+void LLMultiFloater::onClose(bool app_quitting)
+{
+ if(closeAllFloaters() == TRUE)
+ {
+ LLFloater::onClose(app_quitting ? true : false);
+ }//else not all tabs could be closed...
+}
+
+void LLMultiFloater::draw()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ //RN: could this potentially crash in draw hierarchy?
+ close();
+ }
+ else
+ {
+ for (S32 i = 0; i < mTabContainer->getTabCount(); i++)
+ {
+ LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i);
+ if (floaterp->getTitle() != mTabContainer->getPanelTitle(i))
+ {
+ mTabContainer->setPanelTitle(i, floaterp->getTitle());
+ }
+ }
+ LLFloater::draw();
+ }
+}
+
+BOOL LLMultiFloater::closeAllFloaters()
+{
+ S32 tabToClose = 0;
+ S32 lastTabCount = mTabContainer->getTabCount();
+ while (tabToClose < mTabContainer->getTabCount())
+ {
+ LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
+ first_floater->close();
+ if(lastTabCount == mTabContainer->getTabCount())
+ {
+ //Tab did not actually close, possibly due to a pending Save Confirmation dialog..
+ //so try and close the next one in the list...
+ tabToClose++;
+ }else
+ {
+ //Tab closed ok.
+ lastTabCount = mTabContainer->getTabCount();
+ }
+ }
+ if( mTabContainer->getTabCount() != 0 )
+ return FALSE; // Couldn't close all the tabs (pending save dialog?) so return FALSE.
+ return TRUE; //else all tabs were successfully closed...
+}
+
+void LLMultiFloater::growToFit(LLFloater* floaterp, S32 width, S32 height)
+{
+ floater_data_map_t::iterator found_data_it;
+ found_data_it = mFloaterDataMap.find(floaterp->getHandle());
+ if (found_data_it != mFloaterDataMap.end())
+ {
+ // store new width and height with this floater so that it will keep its size when detached
+ found_data_it->second.mWidth = width;
+ found_data_it->second.mHeight = height;
+
+ S32 cur_height = mRect.getHeight();
+ reshape(llmax(mRect.getWidth(), width + LLPANEL_BORDER_WIDTH * 2), llmax(mRect.getHeight(), height + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT + (LLPANEL_BORDER_WIDTH * 2)));
+
+ // make sure upper left corner doesn't move
+ translate(0, mRect.getHeight() - cur_height);
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+}
+
+/**
+ void addFloater(LLFloater* floaterp, BOOL select_added_floater)
+
+ Adds the LLFloater pointed to by floaterp to this.
+ If floaterp is already hosted by this, then it is re-added to get
+ new titles, etc.
+ If select_added_floater is true, the LLFloater pointed to by floaterp will
+ become the selected tab in this
+
+ Affects: mTabContainer, floaterp
+**/
+void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
+{
+ if (!floaterp)
+ {
+ return;
+ }
+
+ if (!mTabContainer)
+ {
+ llerrs << "Tab Container used without having been initialized." << llendl;
+ }
+
+ if (floaterp->getHost() == this)
+ {
+ // already hosted by me, remove
+ // do this so we get updated title, etc.
+ mFloaterDataMap.erase(floaterp->getHandle());
+ mTabContainer->removeTabPanel(floaterp);
+ }
+ else if (floaterp->getHost())
+ {
+ // floaterp is hosted by somebody else and
+ // this is adding it, so remove it from it's old host
+ floaterp->getHost()->removeFloater(floaterp);
+ }
+ else if (floaterp->getParent() == gFloaterView)
+ {
+ // rehost preview floater as child panel
+ gFloaterView->removeChild(floaterp);
+ }
+
+ // store original configuration
+ LLFloaterData floater_data;
+ floater_data.mWidth = floaterp->getRect().getWidth();
+ floater_data.mHeight = floaterp->getRect().getHeight();
+ floater_data.mCanMinimize = floaterp->isMinimizeable();
+ floater_data.mCanResize = floaterp->isResizable();
+
+ // remove minimize and close buttons
+ floaterp->setCanMinimize(FALSE);
+ floaterp->setCanResize(FALSE);
+ floaterp->setCanDrag(FALSE);
+
+ S32 new_width = llmax(mRect.getWidth(), floaterp->getRect().getWidth());
+ S32 new_height = llmax(mRect.getHeight(), floaterp->getRect().getHeight() + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
+
+ reshape(new_width, new_height);
+
+ //add the panel, add it to proper maps
+ mTabContainer->addTabPanel(floaterp, floaterp->getTitle(), FALSE, onTabSelected, this, 0, FALSE, insertion_point);
+ mFloaterDataMap[floaterp->getHandle()] = floater_data;
+
+ if ( select_added_floater )
+ {
+ mTabContainer->selectLastTab();
+ // explicitly call tabopen to load preview assets, etc.
+ tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), true);
+ }
+
+ floaterp->setHost(this);
+ if (mMinimized)
+ {
+ floaterp->setVisible(FALSE);
+ }
+}
+
+/**
+ BOOL selectFloater(LLFloater* floaterp)
+
+ If the LLFloater pointed to by floaterp is hosted by this,
+ then its tab is selected and returns true. Otherwise returns false.
+
+ Affects: mTabContainer
+**/
+BOOL LLMultiFloater::selectFloater(LLFloater* floaterp)
+{
+ return mTabContainer->selectTabPanel(floaterp);
+}
+
+// virtual
+void LLMultiFloater::selectNextFloater()
+{
+ mTabContainer->selectNextTab();
+}
+
+// virtual
+void LLMultiFloater::selectPrevFloater()
+{
+ mTabContainer->selectPrevTab();
+}
+
+void LLMultiFloater::showFloater(LLFloater* floaterp)
+{
+ // we won't select a panel that already is selected
+ // it is hard to do this internally to tab container
+ // as tab selection is handled via index and the tab at a given
+ // index might have changed
+ if (floaterp != mTabContainer->getCurrentPanel() &&
+ !mTabContainer->selectTabPanel(floaterp))
+ {
+ addFloater(floaterp, TRUE);
+ }
+ setVisibleAndFrontmost();
+}
+
+void LLMultiFloater::removeFloater(LLFloater* floaterp)
+{
+ if ( floaterp->getHost() != this )
+ return;
+
+ floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
+ if (found_data_it != mFloaterDataMap.end())
+ {
+ LLFloaterData& floater_data = found_data_it->second;
+ floaterp->setCanMinimize(floater_data.mCanMinimize);
+ if (!floater_data.mCanResize)
+ {
+ // restore original size
+ floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
+ }
+ floaterp->setCanResize(floater_data.mCanResize);
+ mFloaterDataMap.erase(found_data_it);
+ }
+ mTabContainer->removeTabPanel(floaterp);
+ floaterp->setBackgroundVisible(TRUE);
+ floaterp->setHost(NULL);
+
+ if (mAutoResize)
+ {
+ floater_data_map_t::iterator floater_it;
+ S32 new_width = 0;
+ S32 new_height = 0;
+ for (floater_it = mFloaterDataMap.begin(); floater_it != mFloaterDataMap.end(); ++floater_it)
+ {
+ new_width = llmax(new_width, floater_it->second.mWidth + LLPANEL_BORDER_WIDTH * 2);
+ new_height = llmax(new_height, floater_it->second.mHeight + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
+ }
+
+ S32 cur_height = mRect.getHeight();
+
+ reshape(new_width, new_height);
+
+ // make sure upper left corner doesn't move
+ translate(0, cur_height - new_height);
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+
+ tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
+}
+
+void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
+{
+ // default implementation does nothing
+}
+
+void LLMultiFloater::tabClose()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ // no more children, close myself
+ close();
+ }
+}
+
+void LLMultiFloater::setVisible(BOOL visible)
+{
+ //FIXME: shouldn't have to do this, fix adding to minimized multifloater
+ LLFloater::setVisible(visible);
+
+ if (mTabContainer)
+ {
+ LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
+
+ if (cur_floaterp)
+ {
+ cur_floaterp->setVisible(visible);
+ }
+ }
+}
+
+BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getEnabled()
+ && mask == (MASK_CONTROL|MASK_SHIFT))
+ {
+ if (key == 'W')
+ {
+ LLFloater* floater = getActiveFloater();
+ if (floater && floater->canClose())
+ {
+ floater->close();
+ }
+ return TRUE;
+ }
+ }
+
+ return LLFloater::handleKeyHere(key, mask, called_from_parent);
+}
+
+LLFloater* LLMultiFloater::getActiveFloater()
+{
+ return (LLFloater*)mTabContainer->getCurrentPanel();
+}
+
+S32 LLMultiFloater::getFloaterCount()
+{
+ return mTabContainer->getTabCount();
+}
+
+/**
+ BOOL isFloaterFlashing(LLFloater* floaterp)
+
+ Returns true if the LLFloater pointed to by floaterp
+ is currently in a flashing state and is hosted by this.
+ False otherwise.
+
+ Requires: floaterp != NULL
+**/
+BOOL LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ return mTabContainer->getTabPanelFlashing(floaterp);
+
+ return FALSE;
+}
+
+/**
+ BOOL setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
+
+ Sets the current flashing state of the LLFloater pointed
+ to by floaterp to be the BOOL flashing if the LLFloater pointed
+ to by floaterp is hosted by this.
+
+ Requires: floaterp != NULL
+**/
+void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ mTabContainer->setTabPanelFlashing(floaterp, flashing);
+}
+
+//static
+void LLMultiFloater::onTabSelected(void* userdata, bool from_click)
+{
+ LLMultiFloater* floaterp = (LLMultiFloater*)userdata;
+
+ floaterp->tabOpen((LLFloater*)floaterp->mTabContainer->getCurrentPanel(), from_click);
+}
+
+void LLMultiFloater::setCanResize(BOOL can_resize)
+{
+ LLFloater::setCanResize(can_resize);
+ if (mResizable && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+ else
+ {
+ mTabContainer->setRightTabBtnOffset(0);
+ }
+}
+
+BOOL LLMultiFloater::postBuild()
+{
+ if (mTabContainer)
+ {
+ return TRUE;
+ }
+
+ requires("Preview Tabs", WIDGET_TYPE_TAB_CONTAINER);
+ if (checkRequirements())
+ {
+ mTabContainer = LLUICtrlFactory::getTabContainerByName(this, "Preview Tabs");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// virtual
+LLXMLNodePtr LLFloater::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLPanel::getXML();
+
+ node->createChild("title", TRUE)->setStringValue(getTitle());
+
+ node->createChild("can_resize", TRUE)->setBoolValue(isResizable());
+
+ node->createChild("can_minimize", TRUE)->setBoolValue(isMinimizeable());
+
+ node->createChild("can_close", TRUE)->setBoolValue(isCloseable());
+
+ node->createChild("can_drag_on_left", TRUE)->setBoolValue(isDragOnLeft());
+
+ node->createChild("min_width", TRUE)->setIntValue(getMinWidth());
+
+ node->createChild("min_height", TRUE)->setIntValue(getMinHeight());
+
+ node->createChild("can_tear_off", TRUE)->setBoolValue(mCanTearOff);
+
+ return node;
+}
+
+// static
+LLView* LLFloater::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("floater");
+ node->getAttributeString("name", name);
+
+ LLFloater *floaterp = new LLFloater(name);
+
+ LLString filename;
+ node->getAttributeString("filename", filename);
+
+ if (filename.empty())
+ {
+ // Load from node
+ floaterp->initFloaterXML(node, parent, factory);
+ }
+ else
+ {
+ // Load from file
+ factory->buildFloater(floaterp, filename);
+ }
+
+ return floaterp;
+}
+
+void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory, BOOL open)
+{
+ LLString name(getName());
+ LLString title(getTitle());
+ LLString rect_control("");
+ BOOL resizable = isResizable();
+ S32 min_width = getMinWidth();
+ S32 min_height = getMinHeight();
+ BOOL drag_on_left = isDragOnLeft();
+ BOOL minimizable = isMinimizeable();
+ BOOL close_btn = isCloseable();
+ LLRect rect;
+
+ node->getAttributeString("name", name);
+ node->getAttributeString("title", title);
+ node->getAttributeString("rect_control", rect_control);
+ node->getAttributeBOOL("can_resize", resizable);
+ node->getAttributeBOOL("can_minimize", minimizable);
+ node->getAttributeBOOL("can_close", close_btn);
+ node->getAttributeBOOL("can_drag_on_left", drag_on_left);
+ node->getAttributeS32("min_width", min_width);
+ node->getAttributeS32("min_height", min_height);
+
+ if (! rect_control.empty())
+ {
+ setRectControl(rect_control);
+ }
+
+ createRect(node, rect, parent, LLRect());
+
+ setRect(rect);
+ setName(name);
+
+ init(title,
+ resizable,
+ min_width,
+ min_height,
+ drag_on_left,
+ minimizable,
+ close_btn);
+
+ BOOL can_tear_off;
+ if (node->getAttributeBOOL("can_tear_off", can_tear_off))
+ {
+ setCanTearOff(can_tear_off);
+ }
+
+ initFromXML(node, parent);
+
+ LLMultiFloater* last_host = LLFloater::getFloaterHost();
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost((LLMultiFloater*) this);
+ }
+
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ factory->createWidget(this, child);
+ }
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost(last_host);
+ }
+
+
+ BOOL result = postBuild();
+
+ if (!result)
+ {
+ llerrs << "Failed to construct floater " << name << llendl;
+ }
+
+ applyRectControl();
+ if (open)
+ {
+ this->open();
+ }
+}