From 9ec432034dc3c45d7ce763eb02dae4cc7f6b8da8 Mon Sep 17 00:00:00 2001 From: Steven Bennetts Date: Sun, 21 Jun 2009 08:04:56 +0000 Subject: merge -r 122421-124917 viewer-2.0.0-2 -> viewer-2.0.0-3 ignore-dead-branch --- indra/llui/llmultifloater.cpp | 510 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 indra/llui/llmultifloater.cpp (limited to 'indra/llui/llmultifloater.cpp') diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp new file mode 100644 index 0000000000..c0fe7ff32d --- /dev/null +++ b/indra/llui/llmultifloater.cpp @@ -0,0 +1,510 @@ +/** + * @file llmultifloater.cpp + * @brief LLFloater that hosts other floaters + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Floating "windows" within the GL display, like the inventory floater, +// mini-map floater, etc. + +#include "linden_common.h" + +#include "llmultifloater.h" +#include "llresizehandle.h" + +// +// LLMultiFloater +// + +LLMultiFloater::LLMultiFloater(const LLFloater::Params& params) + : LLFloater(), + mTabContainer(NULL), + mTabPos(LLTabContainer::TOP), + mAutoResize(TRUE), + mOrigMinWidth(0), + mOrigMinHeight(0) +{ +} + +void LLMultiFloater::buildTabContainer() +{ + static LLUICachedControl floater_header_size ("UIFloaterHeaderSize", 0); + + LLTabContainer::Params p; + p.name(std::string("Preview Tabs")); + p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0)); + p.tab_position(mTabPos); + p.follows.flags(FOLLOWS_ALL); + p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this)); + + mTabContainer = LLUICtrlFactory::create(p); + addChild(mTabContainer); + + if (isResizable()) + { + mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); + } +} + +void LLMultiFloater::onOpen(const LLSD& key) +{ + if (mTabContainer->getTabCount() <= 0) + { + // for now, don't allow multifloaters + // without any child floaters + closeFloater(); + } +} + +void LLMultiFloater::onClose(bool app_quitting) +{ + if(closeAllFloaters() == TRUE) + { + LLFloater::onClose(app_quitting); + }//else not all tabs could be closed... +} + +void LLMultiFloater::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + //RN: could this potentially crash in draw hierarchy? + closeFloater(); + } + else + { + for (S32 i = 0; i < mTabContainer->getTabCount(); i++) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i); + if (floaterp->getShortTitle() != mTabContainer->getPanelTitle(i)) + { + mTabContainer->setPanelTitle(i, floaterp->getShortTitle()); + } + } + 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->closeFloater(); + 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(S32 content_width, S32 content_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + static LLUICachedControl floater_header_size ("UIFloaterHeaderSize", 0); + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2); + S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height); + + if (isMinimized()) + { + LLRect newrect; + newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height); + setExpandedRect(newrect); + } + else + { + S32 old_height = getRect().getHeight(); + reshape(new_width, new_height); + // keep top left corner in same position + translate(0, old_height - new_height); + } +} + +/** + 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; + return; + } + + 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); + floaterp->storeRectControl(); + // avoid double rendering of floater background (makes it more opaque) + floaterp->setBackgroundVisible(FALSE); + + if (mAutoResize) + { + growToFit(floater_data.mWidth, floater_data.mHeight); + } + + //add the panel, add it to proper maps + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams() + .panel(floaterp) + .label(floaterp->getShortTitle()) + .insert_at(insertion_point)); + mFloaterDataMap[floaterp->getHandle()] = floater_data; + + updateResizeLimits(); + + if ( select_added_floater ) + { + mTabContainer->selectTabPanel(floaterp); + } + else + { + // reassert visible tab (hiding new floater if necessary) + mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex()); + } + + floaterp->setHost(this); + if (isMinimized()) + { + 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, LLTabContainer::eInsertionPoint insertion_point) +{ + // 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, insertion_point); + } +} + +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->setCanDrag(TRUE); + floaterp->setHost(NULL); + floaterp->applyRectControl(); + + updateResizeLimits(); + + 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 + closeFloater(); + } +} + +void LLMultiFloater::setVisible(BOOL visible) +{ + // *FIX: 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); + } + + // if no tab selected, and we're being shown, + // select last tab to be added + if (visible && !cur_floaterp) + { + mTabContainer->selectLastTab(); + } + } +} + +BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask) +{ + if (key == 'W' && mask == MASK_CONTROL) + { + LLFloater* floater = getActiveFloater(); + // is user closeable and is system closeable + if (floater && floater->canClose() && floater->isCloseable()) + { + floater->closeFloater(); + } + return TRUE; + } + + return LLFloater::handleKeyHere(key, mask); +} + +bool LLMultiFloater::addChild(LLView* child, S32 tab_group) +{ + LLTabContainer* tab_container = dynamic_cast(child); + if (tab_container) + { + // store pointer to tab container + setTabContainer(tab_container); + } + + // then go ahead and add child as usual + return LLFloater::addChild(child, tab_group); +} + +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); +} + +void LLMultiFloater::onTabSelected() +{ + LLFloater* floaterp = dynamic_cast(mTabContainer->getCurrentPanel()); + if (floaterp) + { + tabOpen(floaterp, true); + } +} + +void LLMultiFloater::setCanResize(BOOL can_resize) +{ + LLFloater::setCanResize(can_resize); + if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM) + { + mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH); + } + else + { + mTabContainer->setRightTabBtnOffset(0); + } +} + +BOOL LLMultiFloater::postBuild() +{ + // remember any original xml minimum size + getResizeLimits(&mOrigMinWidth, &mOrigMinHeight); + + if (mTabContainer) + { + return TRUE; + } + + requires("Preview Tabs"); + if (checkRequirements()) + { + mTabContainer = getChild("Preview Tabs"); + return TRUE; + } + + return FALSE; +} + +void LLMultiFloater::updateResizeLimits() +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + static LLUICachedControl floater_header_size ("UIFloaterHeaderSize", 0); + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + // initialize minimum size constraint to the original xml values. + S32 new_min_width = mOrigMinWidth; + S32 new_min_height = mOrigMinHeight; + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); + if (floaterp) + { + new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); + } + } + setResizeLimits(new_min_width, new_min_height); + + S32 cur_height = getRect().getHeight(); + S32 new_width = llmax(getRect().getWidth(), new_min_width); + S32 new_height = llmax(getRect().getHeight(), new_min_height); + + if (isMinimized()) + { + const LLRect& expanded = getExpandedRect(); + LLRect newrect; + newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height)); + setExpandedRect(newrect); + } + else + { + reshape(new_width, new_height); + + // make sure upper left corner doesn't move + translate(0, cur_height - getRect().getHeight()); + + // make sure this window is visible on screen when it has been modified + // (tab added, etc) + gFloaterView->adjustToFitScreen(this, TRUE); + } +} -- cgit v1.2.3