diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 09eade34f..8ba3b5367 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -77,6 +77,7 @@ if (VIEWER) add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger) add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) + add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml) # viewer plugins directory add_subdirectory(${LIBS_OPEN_PREFIX}plugins) diff --git a/indra/cmake/LLXUIXML.cmake b/indra/cmake/LLXUIXML.cmake new file mode 100644 index 000000000..b8bfe48c7 --- /dev/null +++ b/indra/cmake/LLXUIXML.cmake @@ -0,0 +1,7 @@ +# -*- cmake -*- + +set(LLXUIXML_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llxuixml + ) + +set(LLXUIXML_LIBRARIES llxuixml) diff --git a/indra/llcharacter/llgesture.cpp b/indra/llcharacter/llgesture.cpp index 83e4e35b0..c23694639 100644 --- a/indra/llcharacter/llgesture.cpp +++ b/indra/llcharacter/llgesture.cpp @@ -1,31 +1,25 @@ /** * @file llgesture.cpp * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcharacter/llgesture.h b/indra/llcharacter/llgesture.h index d394ab763..66b618c47 100644 --- a/indra/llcharacter/llgesture.h +++ b/indra/llcharacter/llgesture.h @@ -3,31 +3,25 @@ * @brief A gesture is a combination of a triggering chat phrase or * key, a sound, an animation, and a chat string. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcharacter/llmultigesture.cpp b/indra/llcharacter/llmultigesture.cpp index 7fe21dbc9..b43554409 100644 --- a/indra/llcharacter/llmultigesture.cpp +++ b/indra/llcharacter/llmultigesture.cpp @@ -2,31 +2,25 @@ * @file llmultigesture.cpp * @brief Gestures that are asset-based and can have multiple steps. * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcharacter/llmultigesture.h b/indra/llcharacter/llmultigesture.h index eb15f600c..0d1c652f9 100644 --- a/indra/llcharacter/llmultigesture.h +++ b/indra/llcharacter/llmultigesture.h @@ -70,6 +70,10 @@ public: KEY mKey; MASK mMask; + // This name can be empty if the inventory item is not around and + // the gesture manager has not yet set the name + std::string mName; + // String, like "/foo" or "hello" that makes it play std::string mTrigger; diff --git a/indra/llcharacter/llvisualparam.cpp b/indra/llcharacter/llvisualparam.cpp index 122406e20..809b312ab 100644 --- a/indra/llcharacter/llvisualparam.cpp +++ b/indra/llcharacter/llvisualparam.cpp @@ -2,31 +2,25 @@ * @file llvisualparam.cpp * @brief Implementation of LLPolyMesh class. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h index 63b9d6893..a70efa9dd 100644 --- a/indra/llcharacter/llvisualparam.h +++ b/indra/llcharacter/llvisualparam.h @@ -2,31 +2,25 @@ * @file llvisualparam.h * @brief Implementation of LLPolyMesh class. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 298308ae8..f6ecbc953 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -223,7 +223,7 @@ void LLPrimitive::setPCode(const U8 p_code) } //=============================================================== -const LLTextureEntry* LLPrimitive::getTE(const U8 index) const +LLTextureEntry* LLPrimitive::getTE(const U8 index) const { return mTextureList.getTexture(index); } diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 126d96f91..7fe47fb01 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -334,7 +334,7 @@ public: // Modify texture entry properties inline BOOL validTE(const U8 te_num) const; - const LLTextureEntry *getTE(const U8 te_num) const; + LLTextureEntry *getTE(const U8 te_num) const; virtual void setNumTEs(const U8 num_tes); virtual void setAllTETextures(const LLUUID &tex_id); diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 42fa88495..92a54d021 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -5,22 +5,26 @@ project(llui) include(00-Common) include(LLCommon) include(LLImage) +include(LLInventory) include(LLMath) include(LLMessage) include(LLRender) include(LLWindow) include(LLVFS) include(LLXML) +include(LLXUIXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} + ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} + ${LLXUIXML_INCLUDE_DIRS} ) set(llui_SOURCE_FILES @@ -75,6 +79,7 @@ set(llui_SOURCE_FILES lluistring.cpp llundo.cpp llviewborder.cpp + llviewmodel.cpp llview.cpp llviewquery.cpp ) @@ -142,6 +147,7 @@ set(llui_HEADER_FILES lluixmltags.h llundo.h llviewborder.h + llviewmodel.h llview.h llviewquery.h ) @@ -159,6 +165,7 @@ target_link_libraries(llui llwindow llimage llvfs # ugh, just for LLDir + llxuixml llxml llcommon # must be after llimage, llwindow, llrender llmath diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index c4bdbfc4a..7c2fd3a81 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -63,47 +63,64 @@ S32 BORDER_SIZE = 1; LLButton::LLButton( const std::string& name, const LLRect& rect, const std::string& control_name, void (*click_callback)(void*), void *callback_data) : LLUICtrl(name, rect, TRUE, NULL, NULL), - mClickedCallback( click_callback ), - mMouseDownCallback( NULL ), - mMouseUpCallback( NULL ), - mHeldDownCallback( NULL ), - mGLFont( NULL ), + mMouseDownFrame( 0 ), - mHeldDownDelay( 0.5f ), // seconds until held-down callback is called - mHeldDownFrameDelay( 0 ), - mImageUnselected( NULL ), - mImageSelected( NULL ), - mImageHoverSelected( NULL ), - mImageHoverUnselected( NULL ), - mImageDisabled( NULL ), - mImageDisabledSelected( NULL ), - mToggleState( FALSE ), - mIsToggle( FALSE ), - mScaleImage( TRUE ), - mDropShadowedText( TRUE ), + mMouseHeldDownCount(0), mBorderEnabled( FALSE ), mFlashing( FALSE ), + mCurGlowStrength(0.f), + mNeedsHighlight(FALSE), + mUnselectedLabel(name), + mSelectedLabel(name), + mGLFont( LLFontGL::getFontSansSerif() ), + mHeldDownDelay( 0.5f ), // seconds until held-down callback is called + mHeldDownFrameDelay( 0 ), + mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), + mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), + mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), + mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), + mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), + mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mImageFlash(NULL), + mImageHoverSelected( NULL ), + mImageHoverUnselected( NULL ), + mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), + mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), + mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), + mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), + mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), + mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), + mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), + mImageOverlay(NULL), + mImageOverlayColor(LLColor4::white), + mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), + mImageOverlaySelectedColor(LLColor4::white), + mImageOverlayAlignment(LLFontGL::HCENTER), + mImageOverlayTopPad(0), + mImageOverlayBottomPad(0), + mImgOverlayLabelSpace(1), + mIsToggle( FALSE ), + mScaleImage( TRUE ), + mDropShadowedText( TRUE ), + mAutoResize( FALSE ), mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), - mHoverGlowStrength(0.15f), - mCurGlowStrength(0.f), - mNeedsHighlight(FALSE), + mBottomVPad( LLBUTTON_V_PAD ), + mHoverGlowStrength(0.25f), mCommitOnReturn(TRUE), - mImagep( NULL ) + mFadeWhenDisabled(FALSE), + mForcePressedState(false), + mDisplayPressedState(TRUE), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL), + mHeldDownSignal(NULL), + mHandleRightMouse(false), + mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), + mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), + mAlpha(1.f) { - mUnselectedLabel = name; - mSelectedLabel = name; - - setImageUnselected(std::string("button_enabled_32x128.tga")); - setImageSelected(std::string("button_enabled_selected_32x128.tga")); - setImageDisabled(std::string("button_disabled_32x128.tga")); - setImageDisabledSelected(std::string("button_disabled_32x128.tga")); - - mImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" ); - mDisabledImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" ); - - init(click_callback, callback_data, NULL, control_name); + init(click_callback, callback_data, control_name); } @@ -117,79 +134,92 @@ LLButton::LLButton(const std::string& name, const LLRect& rect, const std::string& unselected_label, const std::string& selected_label ) : LLUICtrl(name, rect, TRUE, NULL, NULL), - mClickedCallback( click_callback ), - mMouseDownCallback( NULL ), - mMouseUpCallback( NULL ), - mHeldDownCallback( NULL ), - mGLFont( NULL ), mMouseDownFrame( 0 ), + mMouseHeldDownCount(0), + mBorderEnabled( FALSE ), + mFlashing( FALSE ), + mCurGlowStrength(0.f), + mNeedsHighlight(FALSE), + mUnselectedLabel(unselected_label), + mSelectedLabel(selected_label), + mGLFont( font ? font : LLFontGL::getFontSansSerif() ), mHeldDownDelay( 0.5f ), // seconds until held-down callback is called mHeldDownFrameDelay( 0 ), - mImageUnselected( NULL ), - mImageSelected( NULL ), + mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), + mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), + mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), + mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), + mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), + mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mImageFlash(NULL), mImageHoverSelected( NULL ), mImageHoverUnselected( NULL ), - mImageDisabled( NULL ), - mImageDisabledSelected( NULL ), - mToggleState( FALSE ), + mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), + mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), + mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), + mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), + mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), + mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), + mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), + mImageOverlay(NULL), + mImageOverlayColor(LLColor4::white), + mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), + mImageOverlaySelectedColor(LLColor4::white), + mImageOverlayAlignment(LLFontGL::HCENTER), + mImageOverlayTopPad(0), + mImageOverlayBottomPad(0), + mImgOverlayLabelSpace(1), mIsToggle( FALSE ), mScaleImage( TRUE ), mDropShadowedText( TRUE ), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mAutoResize( FALSE ), mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), + mBottomVPad( LLBUTTON_V_PAD ), mHoverGlowStrength(0.25f), - mCurGlowStrength(0.f), - mNeedsHighlight(FALSE), mCommitOnReturn(TRUE), - mImagep( NULL ) + mFadeWhenDisabled(FALSE), + mForcePressedState(false), + mDisplayPressedState(TRUE), + mMouseDownSignal(NULL), + mMouseUpSignal(NULL), + mHeldDownSignal(NULL), + mHandleRightMouse(false), + mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), + mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), + mAlpha(1.f) { - mUnselectedLabel = unselected_label; - mSelectedLabel = selected_label; - - // by default, disabled color is same as enabled - mImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" ); - mDisabledImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" ); if( unselected_image_name != "" ) { // user-specified image - don't use fixed borders unless requested - setImageUnselected(unselected_image_name); - setImageDisabled(unselected_image_name); + mImageUnselected = LLUI::getUIImage(unselected_image_name); + mImageDisabled = mImageUnselected; - mDisabledImageColor.mV[VALPHA] = 0.5f; - mScaleImage = FALSE; - } - else - { - setImageUnselected(std::string("button_enabled_32x128.tga")); - setImageDisabled(std::string("button_disabled_32x128.tga")); + mFadeWhenDisabled = TRUE; + //mScaleImage = FALSE; + + mImagePressedSelected = mImageUnselected; } if( selected_image_name != "" ) { // user-specified image - don't use fixed borders unless requested - setImageSelected(selected_image_name); - setImageDisabledSelected(selected_image_name); + mImageSelected = LLUI::getUIImage(selected_image_name); + mImageDisabledSelected = mImageSelected; - mDisabledImageColor.mV[VALPHA] = 0.5f; - mScaleImage = FALSE; - } - else - { - setImageSelected(std::string("button_enabled_selected_32x128.tga")); - setImageDisabledSelected(std::string("button_disabled_32x128.tga")); + mFadeWhenDisabled = TRUE; + //mScaleImage = FALSE; + + mImagePressed = mImageSelected; } - init(click_callback, callback_data, font, control_name); + init(click_callback, callback_data, control_name); } -void LLButton::init(void (*click_callback)(void*), void *callback_data, const LLFontGL* font, const std::string& control_name) +void LLButton::init(void (*click_callback)(void*), void *callback_data, const std::string& control_name) { - mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); - // Hack to make sure there is space for at least one character if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) { @@ -198,30 +228,22 @@ void LLButton::init(void (*click_callback)(void*), void *callback_data, const LL mRightHPad = LLBUTTON_ORIG_H_PAD; } - mCallbackUserData = callback_data; mMouseDownTimer.stop(); - + + if(click_callback) + setClickedCallback(click_callback, callback_data); + setControlName(control_name, NULL); - mUnselectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelColor" ) ); - mSelectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelSelectedColor" ) ); - mDisabledLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelDisabledColor" ) ); - mDisabledSelectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelSelectedDisabledColor" ) ); - mHighlightColor = ( LLUI::sColorsGroup->getColor( "ButtonUnselectedFgColor" ) ); - mUnselectedBgColor = ( LLUI::sColorsGroup->getColor( "ButtonUnselectedBgColor" ) ); - mSelectedBgColor = ( LLUI::sColorsGroup->getColor( "ButtonSelectedBgColor" ) ); - mFlashBgColor = ( LLUI::sColorsGroup->getColor( "ButtonFlashBgColor" ) ); - - mImageOverlayAlignment = LLFontGL::HCENTER; - mImageOverlayColor = LLColor4::white; } + +// virtual LLButton::~LLButton() { - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - } + delete mMouseDownSignal; + delete mMouseUpSignal; + delete mHeldDownSignal; } // HACK: Committing a button is the same as instantly clicking it. @@ -229,19 +251,12 @@ LLButton::~LLButton() void LLButton::onCommit() { // WARNING: Sometimes clicking a button destroys the floater or - // panel containing it. Therefore we need to call mClickedCallback + // panel containing it. Therefore we need to call LLUICtrl::onCommit() // LAST, otherwise this becomes deleted memory. - LLUICtrl::onCommit(); - if (mMouseDownCallback) - { - (*mMouseDownCallback)(mCallbackUserData); - } + if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); - if (mMouseUpCallback) - { - (*mMouseUpCallback)(mCallbackUserData); - } + if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); if (getSoundFlags() & MOUSE_DOWN) { @@ -259,13 +274,66 @@ void LLButton::onCommit() } // do this last, as it can result in destroying this button - if (mClickedCallback) - { - (*mClickedCallback)( mCallbackUserData ); - } + LLUICtrl::onCommit(); +} + +/*boost::signals2::connection LLButton::setClickedCallback(const CommitCallbackParam& cb) +{ + return setClickedCallback(initCommitCallback(cb)); +} +boost::signals2::connection LLButton::setMouseDownCallback(const CommitCallbackParam& cb) +{ + return setMouseDownCallback(initCommitCallback(cb)); +} +boost::signals2::connection LLButton::setMouseUpCallback(const CommitCallbackParam& cb) +{ + return setMouseUpCallback(initCommitCallback(cb)); +} +boost::signals2::connection LLButton::setHeldDownCallback(const CommitCallbackParam& cb) +{ + return setHeldDownCallback(initCommitCallback(cb)); +}*/ + + +boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mCommitSignal) mCommitSignal = new commit_signal_t(); + return mCommitSignal->connect(cb); +} +boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t(); + return mMouseDownSignal->connect(cb); +} +boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t(); + return mMouseUpSignal->connect(cb); +} +boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t(); + return mHeldDownSignal->connect(cb); } +// *TODO: Deprecate (for backwards compatibility only) +boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data ) +{ + return setClickedCallback(boost::bind(cb, data)); +} +boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data ) +{ + return setMouseDownCallback(boost::bind(cb, data)); +} +boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data ) +{ + return setMouseUpCallback(boost::bind(cb, data)); +} +boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data ) +{ + return setHeldDownCallback(boost::bind(cb, data)); +} BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) { @@ -278,10 +346,8 @@ BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) toggleState(); } - if (mClickedCallback) - { - (*mClickedCallback)( mCallbackUserData ); - } + LLUICtrl::onCommit(); + handled = TRUE; } return handled; @@ -299,10 +365,7 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask ) handled = TRUE; - if (mClickedCallback) - { - (*mClickedCallback)( mCallbackUserData ); - } + LLUICtrl::onCommit(); } return handled; } @@ -310,27 +373,35 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask ) BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) { - // Route future Mouse messages here preemptively. (Release on mouse up.) - gFocusMgr.setMouseCapture( this ); - - if (hasTabStop() && !getIsChrome()) + if (!childrenHandleMouseDown(x, y, mask)) { - setFocus(TRUE); - } + // Route future Mouse messages here preemptively. (Release on mouse up.) + gFocusMgr.setMouseCapture( this ); - if (mMouseDownCallback) - { - (*mMouseDownCallback)(mCallbackUserData); - } + if (hasTabStop() && !getIsChrome()) + { + setFocus(TRUE); + } + + /* + * ATTENTION! This call fires another mouse down callback. + * If you wish to remove this call emit that signal directly + * by calling LLUICtrl::mMouseDownSignal(x, y, mask); + */ + LLUICtrl::handleMouseDown(x, y, mask); + + if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD()); mMouseDownTimer.start(); - mMouseDownFrame = (S32) LLFrameTimer::getFrameCount(); - - if (getSoundFlags() & MOUSE_DOWN) - { - make_ui_sound("UISndClick"); - } + mMouseDownFrame = (S32) LLFrameTimer::getFrameCount(); + mMouseHeldDownCount = 0; + + if (getSoundFlags() & MOUSE_DOWN) + { + make_ui_sound("UISndClick"); + } + } return TRUE; } @@ -343,14 +414,17 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) // Always release the mouse gFocusMgr.setMouseCapture( NULL ); - // Regardless of where mouseup occurs, handle callback - if (mMouseUpCallback) - { - (*mMouseUpCallback)(mCallbackUserData); - } + /* + * ATTENTION! This call fires another mouse up callback. + * If you wish to remove this call emit that signal directly + * by calling LLUICtrl::mMouseUpSignal(x, y, mask); + */ + LLUICtrl::handleMouseUp(x, y, mask); - mMouseDownTimer.stop(); - mMouseDownTimer.reset(); + // Regardless of where mouseup occurs, handle callback + if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + resetMouseDownTimer(); // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked. // If mouseup in the widget, it's been clicked @@ -366,83 +440,167 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) toggleState(); } - if (mClickedCallback) - { - (*mClickedCallback)( mCallbackUserData ); - } + LLUICtrl::onCommit(); } } + else + { + childrenHandleMouseUp(x, y, mask); + } return TRUE; } +BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask)) + { + // Route future Mouse messages here preemptively. (Release on mouse up.) + gFocusMgr.setMouseCapture( this ); + + if (hasTabStop() && !getIsChrome()) + { + setFocus(TRUE); + } + +// if (pointInView(x, y)) +// { +// } + // send the mouse down signal + LLUICtrl::handleRightMouseDown(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way + // if they are not mouse opaque. + } + + return TRUE; +} + +BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + if (mHandleRightMouse) + { + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + // Always release the mouse + gFocusMgr.setMouseCapture( NULL ); + + // if (pointInView(x, y)) + // { + // mRightMouseUpSignal(this, x,y,mask); + // } + } + else + { + childrenHandleRightMouseUp(x, y, mask); + } + + // send the mouse up signal + LLUICtrl::handleRightMouseUp(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way. + // if they are not mouse opaque. + } + return TRUE; +} + +void LLButton::setHighlight(bool b) +{ + mNeedsHighlight = b; +} BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) { - LLMouseHandler* other_captor = gFocusMgr.getMouseCapture(); - mNeedsHighlight = other_captor == NULL || - other_captor == this || - // this following bit is to support modal dialogs - (other_captor->isView() && hasAncestor((LLView*)other_captor)); + if (isInEnabledChain() + && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)) + mNeedsHighlight = TRUE; - if (mMouseDownTimer.getStarted() && NULL != mHeldDownCallback) + if (!childrenHandleHover(x, y, mask)) { - F32 elapsed = getHeldDownTime(); - if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame) + if (mMouseDownTimer.getStarted()) { - mHeldDownCallback( mCallbackUserData ); + F32 elapsed = getHeldDownTime(); + if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame) + { + LLSD param; + param["count"] = mMouseHeldDownCount++; + if (mHeldDownSignal) (*mHeldDownSignal)(this, param); + } } + + // We only handle the click if the click both started and ended within us + getWindow()->setCursor(UI_CURSOR_ARROW); + lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; } - - // We only handle the click if the click both started and ended within us - getWindow()->setCursor(UI_CURSOR_ARROW); - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - return TRUE; } +void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height) +{ + overlay_width = mImageOverlay->getWidth(); + overlay_height = mImageOverlay->getHeight(); + + F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f); + overlay_width = llround((F32)overlay_width * scale_factor); + overlay_height = llround((F32)overlay_height * scale_factor); +} + // virtual void LLButton::draw() { - BOOL flash = FALSE; + F32 alpha = mAlpha; + bool flash = FALSE; if( mFlashing ) { F32 elapsed = mFlashingTimer.getElapsedTimeF32(); - S32 flash_count = S32(elapsed * LLUI::sConfigGroup->getF32("ButtonFlashRate") * 2.f); + S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f); // flash on or off? - flash = (flash_count % 2 == 0) || flash_count > S32((F32)LLUI::sConfigGroup->getS32("ButtonFlashCount") * 2.f); + flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f); } - BOOL pressed_by_keyboard = FALSE; + bool pressed_by_keyboard = FALSE; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); } - // Unselected image assignments - S32 local_mouse_x; - S32 local_mouse_y; - LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); + bool mouse_pressed_and_over = false; + if (hasMouseCapture()) + { + S32 local_mouse_x ; + S32 local_mouse_y; + LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); + mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y); + } - BOOL pressed = pressed_by_keyboard - || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) - || mToggleState; + bool enabled = isInEnabledChain(); - BOOL use_glow_effect = FALSE; + bool pressed = pressed_by_keyboard + || mouse_pressed_and_over + || mForcePressedState; + bool selected = getToggleState(); + + bool use_glow_effect = FALSE; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; - if ( mNeedsHighlight ) + LLUIImage* imagep = NULL; + if (pressed && mDisplayPressedState) { - if (pressed) + imagep = selected ? mImagePressedSelected : mImagePressed; + } + else if ( mNeedsHighlight ) + { + if (selected) { if (mImageHoverSelected) { - mImagep = mImageHoverSelected; + imagep = mImageHoverSelected; } else { - mImagep = mImageSelected; + imagep = mImageSelected; use_glow_effect = TRUE; } } @@ -450,32 +608,18 @@ void LLButton::draw() { if (mImageHoverUnselected) { - mImagep = mImageHoverUnselected; + imagep = mImageHoverUnselected; } else { - mImagep = mImageUnselected; + imagep = mImageUnselected; use_glow_effect = TRUE; } } } - else if ( pressed ) + else { - mImagep = mImageSelected; - } - else - { - mImagep = mImageUnselected; - } - - if (mFlashing) - { - use_glow_effect = TRUE; - glow_type = LLRender::BT_ALPHA; // blend the glow - if (mNeedsHighlight) // highlighted AND flashing - glow_color = (glow_color*0.5f + mFlashBgColor*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity - else - glow_color = mFlashBgColor; + imagep = selected ? mImageSelected : mImageUnselected; } // Override if more data is available @@ -485,19 +629,41 @@ void LLButton::draw() // disabled but checked if (!mImageDisabledSelected.isNull() && - ( (getEnabled() && getTentative()) - || (!getEnabled() && pressed ) ) ) + ( (enabled && getTentative()) + || (!enabled && selected ) ) ) { - mImagep = mImageDisabledSelected; + imagep = mImageDisabledSelected; } else if (!mImageDisabled.isNull() - && !getEnabled() - && !pressed) + && !enabled + && !selected) { - mImagep = mImageDisabled; + imagep = mImageDisabled; } - if (mNeedsHighlight && !mImagep) + if (mFlashing) + { + // if button should flash and we have icon for flashing, use it as image for button + if(flash && mImageFlash) + { + // setting flash to false to avoid its further influence on glow + flash = false; + imagep = mImageFlash; + } + // else use usual flashing via flash_color + else + { + LLColor4 flash_color = mFlashBgColor.get(); + use_glow_effect = TRUE; + glow_type = LLRender::BT_ALPHA; // blend the glow + if (mNeedsHighlight) // highlighted AND flashing + glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity + else + glow_color = flash_color; + } + } + + if (mNeedsHighlight && !imagep) { use_glow_effect = TRUE; } @@ -506,60 +672,37 @@ void LLButton::draw() LLColor4 label_color; // label changes when button state changes, not when pressed - if ( getEnabled() ) + if ( enabled ) { - if ( mToggleState ) + if ( getToggleState() ) { - label_color = mSelectedLabelColor; + label_color = mSelectedLabelColor.get(); } else { - label_color = mUnselectedLabelColor; + label_color = mUnselectedLabelColor.get(); } } else { - if ( mToggleState ) + if ( getToggleState() ) { - label_color = mDisabledSelectedLabelColor; + label_color = mDisabledSelectedLabelColor.get(); } else { - label_color = mDisabledLabelColor; + label_color = mDisabledLabelColor.get(); } } // Unselected label assignments - LLWString label; - - if( mToggleState ) - { - if( getEnabled() || mDisabledSelectedLabel.empty() ) - { - label = mSelectedLabel; - } - else - { - label = mDisabledSelectedLabel; - } - } - else - { - if( getEnabled() || mDisabledLabel.empty() ) - { - label = mUnselectedLabel; - } - else - { - label = mDisabledLabel; - } - } + LLWString label = getCurrentLabel(); // overlay with keyboard focus border if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); - drawBorder(gFocusMgr.getFocusColor(), llround(lerp(1.f, 3.f, lerp_amt))); + drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) @@ -576,25 +719,27 @@ void LLButton::draw() // Draw button image, if available. // Otherwise draw basic rectangular button. - if (mImagep.notNull()) + if (imagep != NULL) { + // apply automatic 50% alpha fade to disabled image + LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get(); if ( mScaleImage) { - mImagep->draw(getLocalRect(), getEnabled() ? mImageColor : mDisabledImageColor ); + imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); - mImagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % mCurGlowStrength); + imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } else { - mImagep->draw(0, 0, getEnabled() ? mImageColor : mDisabledImageColor ); + imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); - mImagep->drawSolid(0, 0, glow_color % mCurGlowStrength); + imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } @@ -602,9 +747,9 @@ void LLButton::draw() else { // no image - llwarns << "No image for button " << getName() << llendl; + lldebugs << "No image for button " << getName() << llendl; // draw it in pink so we can find it - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1, FALSE); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE); } // let overlay image and text play well together @@ -616,37 +761,41 @@ void LLButton::draw() if (mImageOverlay.notNull() && mImageOverlay->getWidth() > 1) { // get max width and height (discard level 0) - S32 overlay_width = mImageOverlay->getWidth(); - S32 overlay_height = mImageOverlay->getHeight(); + S32 overlay_width; + S32 overlay_height; - F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f); - overlay_width = llround((F32)overlay_width * scale_factor); - overlay_height = llround((F32)overlay_height * scale_factor); + getOverlayImageSize(overlay_width, overlay_height); S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); //FUGLY HACK FOR "DEPRESSED" BUTTONS - if (pressed) + if (pressed && mDisplayPressedState) { center_y--; center_x++; } + center_y += (mImageOverlayBottomPad - mImageOverlayTopPad); // fade out overlay images on disabled buttons - LLColor4 overlay_color = mImageOverlayColor; - if (!getEnabled()) + LLColor4 overlay_color = mImageOverlayColor.get(); + if (!enabled) { - overlay_color.mV[VALPHA] = 0.5f; + overlay_color = mImageOverlayDisabledColor.get(); } + else if (getToggleState()) + { + overlay_color = mImageOverlaySelectedColor.get(); + } + overlay_color.mV[VALPHA] *= alpha; switch(mImageOverlayAlignment) { case LLFontGL::LEFT: - text_left += overlay_width + 1; - text_width -= overlay_width + 1; + text_left += overlay_width + mImgOverlayLabelSpace; + text_width -= overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( - mLeftHPad, + mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, @@ -661,10 +810,10 @@ void LLButton::draw() overlay_color); break; case LLFontGL::RIGHT: - text_right -= overlay_width + 1; - text_width -= overlay_width + 1; + text_right -= overlay_width + mImgOverlayLabelSpace; + text_width -= overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( - getRect().getWidth() - mRightHPad - overlay_width, + getRect().getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, @@ -698,7 +847,7 @@ void LLButton::draw() S32 y_offset = 2 + (getRect().getHeight() - 20)/2; - if (pressed) + if (pressed && mDisplayPressedState) { y_offset--; x++; @@ -706,8 +855,8 @@ void LLButton::draw() mGLFont->render(label, 0, (F32)x, - (F32)(LLBUTTON_V_PAD + y_offset), - label_color, + (F32)(mBottomVPad + y_offset), + label_color % alpha, mHAlign, LLFontGL::BOTTOM, LLFontGL::NORMAL, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, @@ -720,45 +869,45 @@ void LLButton::draw() { drawDebugRect(); } - + // reset hover status for next frame mNeedsHighlight = FALSE; + + LLUICtrl::draw(); } -void LLButton::drawBorder(const LLColor4& color, S32 size) +void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size) { + if (imagep == NULL) return; if (mScaleImage) { - mImagep->drawBorder(getLocalRect(), color, size); + imagep->drawBorder(getLocalRect(), color, size); } else { - mImagep->drawBorder(0, 0, color, size); + imagep->drawBorder(0, 0, color, size); } } -void LLButton::setClickedCallback(void (*cb)(void*), void* userdata) +BOOL LLButton::getToggleState() const { - mClickedCallback = cb; - if (userdata) - { - mCallbackUserData = userdata; - } + return getValue().asBoolean(); } - void LLButton::setToggleState(BOOL b) { - if( b != mToggleState ) + if( b != getToggleState() ) { setControlValue(b); // will fire LLControlVariable callbacks (if any) - mToggleState = b; // may or may not be redundant + setValue(b); // may or may not be redundant + // Unselected label assignments + autoResize(); } } void LLButton::setFlashing( BOOL b ) { - if (b != mFlashing) + if ((bool)b != mFlashing) { mFlashing = b; mFlashingTimer.reset(); @@ -768,18 +917,10 @@ void LLButton::setFlashing( BOOL b ) BOOL LLButton::toggleState() { - setToggleState( !mToggleState ); - return mToggleState; -} + bool flipped = ! getToggleState(); + setToggleState(flipped); -void LLButton::setValue(const LLSD& value ) -{ - mToggleState = value.asBoolean(); -} - -LLSD LLButton::getValue() const -{ - return mToggleState == TRUE; + return flipped; } void LLButton::setLabel( const LLStringExplicit& label ) @@ -806,26 +947,72 @@ void LLButton::setLabelSelected( const LLStringExplicit& label ) mSelectedLabel = label; } -void LLButton::setDisabledLabel( const LLStringExplicit& label ) +const LLUIString& LLButton::getCurrentLabel() const { - mDisabledLabel = label; -} - -void LLButton::setDisabledSelectedLabel( const LLStringExplicit& label ) -{ - mDisabledSelectedLabel = label; + if( getToggleState() ) + { + return mSelectedLabel; + } + else + { + return mUnselectedLabel; + } } void LLButton::setImageUnselected(LLPointer image) { mImageUnselected = image; + if (mImageUnselected.isNull()) + { + llwarns << "Setting default button image for: " << getName() << " to NULL" << llendl; + } } +void LLButton::autoResize() +{ + resize(getCurrentLabel()); +} + +void LLButton::resize(LLUIString label) +{ + // get label length + S32 label_width = mGLFont->getWidth(label.getString()); + // get current btn length + S32 btn_width =getRect().getWidth(); + // check if it need resize + if (mAutoResize) + { + S32 min_width = label_width + mLeftHPad + mRightHPad; + if (mImageOverlay) + { + S32 overlay_width = mImageOverlay->getWidth(); + F32 scale_factor = (getRect().getHeight() - (mImageOverlayBottomPad + mImageOverlayTopPad)) / (F32)mImageOverlay->getHeight(); + overlay_width = llround((F32)overlay_width * scale_factor); + + switch(mImageOverlayAlignment) + { + case LLFontGL::LEFT: + case LLFontGL::RIGHT: + min_width += overlay_width + mImgOverlayLabelSpace; + break; + case LLFontGL::HCENTER: + min_width = llmax(min_width, overlay_width + mLeftHPad + mRightHPad); + break; + default: + // draw nothing + break; + } + } + if (btn_width < min_width) + { + reshape(min_width, getRect().getHeight()); + } + } +} void LLButton::setImages( const std::string &image_name, const std::string &selected_name ) { - setImageUnselected(image_name); - setImageSelected(selected_name); - + setImageUnselected(LLUI::getUIImage(image_name)); + setImageSelected(LLUI::getUIImage(selected_name)); } void LLButton::setImageSelected(LLPointer image) @@ -843,31 +1030,18 @@ void LLButton::setColor(const LLColor4& color) setImageColor(color); } -void LLButton::setAlpha(F32 alpha) -{ - mImageColor.setAlpha(alpha); - mDisabledImageColor.setAlpha(alpha * 0.5f); -} - void LLButton::setImageDisabled(LLPointer image) { mImageDisabled = image; mDisabledImageColor = mImageColor; - mDisabledImageColor.mV[VALPHA] *= 0.5f; + mFadeWhenDisabled = TRUE; } void LLButton::setImageDisabledSelected(LLPointer image) { mImageDisabledSelected = image; mDisabledImageColor = mImageColor; - mDisabledImageColor.mV[VALPHA] *= 0.5f; -} - -void LLButton::setDisabledImages( const std::string &image_name, const std::string &selected_name, const LLColor4& c ) -{ - setImageDisabled(image_name); - setImageDisabledSelected(selected_name); - mDisabledImageColor = c; + mFadeWhenDisabled = TRUE; } void LLButton::setImageHoverSelected(LLPointer image) @@ -875,22 +1049,14 @@ void LLButton::setImageHoverSelected(LLPointer image) mImageHoverSelected = image; } -void LLButton::setDisabledImages( const std::string &image_name, const std::string &selected_name) -{ - LLColor4 clr = mImageColor; - clr.mV[VALPHA] *= .5f; - setDisabledImages( image_name, selected_name, clr ); -} - void LLButton::setImageHoverUnselected(LLPointer image) { mImageHoverUnselected = image; } -void LLButton::setHoverImages( const std::string& image_name, const std::string& selected_name ) +void LLButton::setImageFlash(LLPointer image) { - setImageHoverUnselected(image_name); - setImageHoverSelected(selected_name); + mImageFlash = image; } void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color) @@ -907,11 +1073,23 @@ void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign a } } +void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color) +{ + if (image_id.isNull()) + { + mImageOverlay = NULL; + } + else + { + mImageOverlay = LLUI::getUIImageByID(image_id); + mImageOverlayAlignment = alignment; + mImageOverlayColor = color; + } +} void LLButton::onMouseCaptureLost() { - mMouseDownTimer.stop(); - mMouseDownTimer.reset(); + resetMouseDownTimer(); } //------------------------------------------------------------------------- @@ -932,55 +1110,23 @@ S32 round_up(S32 grid, S32 value) } } -void LLButton::setImageUnselected(const std::string &image_name) -{ - setImageUnselected(LLUI::getUIImage(image_name)); - mImageUnselectedName = image_name; -} - -void LLButton::setImageSelected(const std::string &image_name) -{ - setImageSelected(LLUI::getUIImage(image_name)); - mImageSelectedName = image_name; -} - -void LLButton::setImageHoverSelected(const std::string &image_name) -{ - setImageHoverSelected(LLUI::getUIImage(image_name)); - mImageHoverSelectedName = image_name; -} - -void LLButton::setImageHoverUnselected(const std::string &image_name) -{ - setImageHoverUnselected(LLUI::getUIImage(image_name)); - mImageHoverUnselectedName = image_name; -} - -void LLButton::setImageDisabled(const std::string &image_name) -{ - setImageDisabled(LLUI::getUIImage(image_name)); - mImageDisabledName = image_name; -} - -void LLButton::setImageDisabledSelected(const std::string &image_name) -{ - setImageDisabledSelected(LLUI::getUIImage(image_name)); - mImageDisabledSelectedName = image_name; -} - void LLButton::addImageAttributeToXML(LLXMLNodePtr node, - const std::string& image_name, - const LLUUID& image_id, + const LLPointer image, const std::string& xml_tag_name) const { - if( !image_name.empty() ) - { + if(!image) + return; + + const std::string image_name = image->getName(); + + if(image_name.empty()) + return; + + LLUUID id; + if(!id.set(image_name, false)) node->createChild(xml_tag_name.c_str(), TRUE)->setStringValue(image_name); - } - else if( image_id != LLUUID::null ) - { - node->createChild((xml_tag_name + "_id").c_str(), TRUE)->setUUIDValue(image_id); - } + else + node->createChild((xml_tag_name + "_id").c_str(), TRUE)->setUUIDValue(id); } // virtual @@ -995,12 +1141,12 @@ LLXMLNodePtr LLButton::getXML(bool save_children) const node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont)); node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign)); - addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,std::string("image_unselected")); - addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,std::string("image_selected")); - addImageAttributeToXML(node,mImageHoverSelectedName,mImageHoverSelectedID,std::string("image_hover_selected")); - addImageAttributeToXML(node,mImageHoverUnselectedName,mImageHoverUnselectedID,std::string("image_hover_unselected")); - addImageAttributeToXML(node,mImageDisabledName,mImageDisabledID,std::string("image_disabled")); - addImageAttributeToXML(node,mImageDisabledSelectedName,mImageDisabledSelectedID,std::string("image_disabled_selected")); + addImageAttributeToXML(node,mImageUnselected,std::string("image_unselected")); + addImageAttributeToXML(node,mImageSelected,std::string("image_selected")); + addImageAttributeToXML(node,mImageHoverSelected,std::string("image_hover_selected")); + addImageAttributeToXML(node,mImageHoverUnselected,std::string("image_hover_unselected")); + addImageAttributeToXML(node,mImageDisabled,std::string("image_disabled")); + addImageAttributeToXML(node,mImageDisabledSelected,std::string("image_disabled_selected")); node->createChild("scale_image", TRUE)->setBoolValue(mScaleImage); @@ -1082,13 +1228,13 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa node->getAttributeBOOL("toggle", is_toggle); button->setIsToggle(is_toggle); - if(image_hover_selected != LLStringUtil::null) button->setImageHoverSelected(image_hover_selected); + if(image_hover_selected != LLStringUtil::null) button->setImageHoverSelected(LLUI::getUIImage(image_hover_selected)); - if(image_hover_unselected != LLStringUtil::null) button->setImageHoverUnselected(image_hover_unselected); + if(image_hover_unselected != LLStringUtil::null) button->setImageHoverUnselected(LLUI::getUIImage(image_hover_unselected)); - if(image_disabled_selected != LLStringUtil::null) button->setImageDisabledSelected(image_disabled_selected ); + if(image_disabled_selected != LLStringUtil::null) button->setImageDisabledSelected(LLUI::getUIImage(image_disabled_selected)); - if(image_disabled != LLStringUtil::null) button->setImageDisabled(image_disabled); + if(image_disabled != LLStringUtil::null) button->setImageDisabled(LLUI::getUIImage(image_disabled)); if(image_overlay != LLStringUtil::null) button->setImageOverlay(image_overlay, image_overlay_alignment); @@ -1126,6 +1272,18 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa return button; } +void LLButton::resetMouseDownTimer() +{ + mMouseDownTimer.stop(); + mMouseDownTimer.reset(); +} + +BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + // just treat a double click as a second click + return handleMouseDown(x, y, mask); +} + void LLButton::setHelpURLCallback(const std::string &help_url) { mHelpURL = help_url; diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index d9fde0ea8..039ee034b 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -41,6 +41,8 @@ #include "llfontgl.h" #include "lluiimage.h" #include "lluistring.h" +#include "llinitparam.h" +#include "lluicolor.h" // // Constants @@ -85,21 +87,26 @@ public: const LLFontGL* mGLFont = NULL, const std::string& unselected_label = LLStringUtil::null, const std::string& selected_label = LLStringUtil::null ); +public: - virtual ~LLButton(); - void init(void (*click_callback)(void*), void *callback_data, const LLFontGL* font, const std::string& control_name); + ~LLButton(); + // For backward compatability only + typedef boost::function button_callback_t; - - void addImageAttributeToXML(LLXMLNodePtr node, const std::string& imageName, - const LLUUID& imageID,const std::string& xmlTagName) const; + void addImageAttributeToXML(LLXMLNodePtr node, const LLPointer, const std::string& xmlTagName) const; + void init(void (*click_callback)(void*), void *callback_data, const std::string& control_name); virtual LLXMLNodePtr getXML(bool save_children = true) const; - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + virtual void setAlpha( F32 alpha ) { mAlpha = alpha; } virtual BOOL handleUnicodeCharHere(llwchar uni_char); virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); virtual void draw(); virtual void onMouseCaptureLost(); @@ -109,29 +116,48 @@ public: void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; } void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; } - void setClickedCallback( void (*cb)(void *data), void* data = NULL ); // mouse down and up within button - void setMouseDownCallback( void (*cb)(void *data) ) { mMouseDownCallback = cb; } // mouse down within button - void setMouseUpCallback( void (*cb)(void *data) ) { mMouseUpCallback = cb; } // mouse up, EVEN IF NOT IN BUTTON - void setHeldDownCallback( void (*cb)(void *data) ) { mHeldDownCallback = cb; } // Mouse button held down and in button + + /*boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb); + boost::signals2::connection setMouseDownCallback(const CommitCallbackParam& cb); + boost::signals2::connection setMouseUpCallback(const CommitCallbackParam& cb); + boost::signals2::connection setHeldDownCallback(const CommitCallbackParam& cb);*/ + + boost::signals2::connection setClickedCallback( const commit_signal_t::slot_type& cb ); // mouse down and up within button + boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); // mouse up, EVEN IF NOT IN BUTTON + // Passes a 'count' parameter in the commit param payload, i.e. param["count"]) + boost::signals2::connection setHeldDownCallback( const commit_signal_t::slot_type& cb ); // Mouse button held down and in button + + + // *TODO: Deprecate (for backwards compatability only) + boost::signals2::connection setClickedCallback( button_callback_t cb, void* data ); + boost::signals2::connection setMouseDownCallback( button_callback_t cb, void* data ); + boost::signals2::connection setMouseUpCallback( button_callback_t cb, void* data ); + boost::signals2::connection setHeldDownCallback( button_callback_t cb, void* data ); + void setHeldDownDelay( F32 seconds, S32 frames = 0) { mHeldDownDelay = seconds; mHeldDownFrameDelay = frames; } F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); } - BOOL getIsToggle() const { return mIsToggle; } - void setIsToggle(BOOL is_toggle) { mIsToggle = is_toggle; } BOOL toggleState(); - BOOL getToggleState() const { return mToggleState; } + BOOL getToggleState() const; void setToggleState(BOOL b); + void setHighlight(bool b); void setFlashing( BOOL b ); BOOL getFlashing() const { return mFlashing; } void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } LLFontGL::HAlign getHAlign() const { return mHAlign; } void setLeftHPad( S32 pad ) { mLeftHPad = pad; } - S32 getLeftHPad() const { return mLeftHPad; } + S32 getLeftHPad() const { return mLeftHPad; } void setRightHPad( S32 pad ) { mRightHPad = pad; } - S32 getRightHPad() const { return mRightHPad; } + S32 getRightHPad() const { return mRightHPad; } + + void setImageOverlayTopPad( S32 pad ) { mImageOverlayTopPad = pad; } + S32 getImageOverlayTopPad() const { return mImageOverlayTopPad; } + void setImageOverlayBottomPad( S32 pad ) { mImageOverlayBottomPad = pad; } + S32 getImageOverlayBottomPad() const { return mImageOverlayBottomPad; } const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); } const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); } @@ -139,35 +165,31 @@ public: void setImageColor(const std::string& color_control); void setImageColor(const LLColor4& c); /*virtual*/ void setColor(const LLColor4& c); - /*virtual*/ void setAlpha(F32 alpha); void setImages(const std::string &image_name, const std::string &selected_name); - void setDisabledImages(const std::string &image_name, const std::string &selected_name); - void setDisabledImages(const std::string &image_name, const std::string &selected_name, const LLColor4& c); - - void setHoverImages(const std::string &image_name, const std::string &selected_name); void setDisabledImageColor(const LLColor4& c) { mDisabledImageColor = c; } void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; } void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); + void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); LLPointer getImageOverlay() { return mImageOverlay; } + LLFontGL::HAlign getImageOverlayHAlign() const { return mImageOverlayAlignment; } - - virtual void setValue(const LLSD& value ); - virtual LLSD getValue() const; - + void autoResize(); // resize with label of current btn state + void resize(LLUIString label); // resize with label input void setLabel( const LLStringExplicit& label); virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); void setLabelUnselected(const LLStringExplicit& label); void setLabelSelected(const LLStringExplicit& label); - void setDisabledLabel(const LLStringExplicit& disabled_label); - void setDisabledSelectedLabel(const LLStringExplicit& disabled_label); void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; } void setFont(const LLFontGL *font) { mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); } + const LLFontGL* getFont() const { return mGLFont; } + const LLUIString& getCurrentLabel() const; + void setScaleImage(BOOL scale) { mScaleImage = scale; } BOOL getScaleImage() const { return mScaleImage; } @@ -175,132 +197,130 @@ public: void setBorderEnabled(BOOL b) { mBorderEnabled = b; } - static void onHeldDown(void *userdata); // to be called by gIdleCallbacks - void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } - void setImageUnselected(const std::string &image_name); - const std::string& getImageUnselectedName() const { return mImageUnselectedName; } - void setImageSelected(const std::string &image_name); - const std::string& getImageSelectedName() const { return mImageSelectedName; } - void setImageHoverSelected(const std::string &image_name); - void setImageHoverUnselected(const std::string &image_name); - void setImageDisabled(const std::string &image_name); - void setImageDisabledSelected(const std::string &image_name); - void setImageUnselected(LLPointer image); void setImageSelected(LLPointer image); void setImageHoverSelected(LLPointer image); void setImageHoverUnselected(LLPointer image); void setImageDisabled(LLPointer image); void setImageDisabledSelected(LLPointer image); - + void setImageFlash(LLPointer image); + void setImagePressed(LLPointer image); + void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } BOOL getCommitOnReturn() const { return mCommitOnReturn; } + static void onHeldDown(void *userdata); // to be called by gIdleCallbacks void setHelpURLCallback(const std::string &help_url); const std::string& getHelpURL() const { return mHelpURL; } + void setForcePressedState(bool b) { mForcePressedState = b; } + + void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; } + + bool getIsToggle() const { return mIsToggle; } + bool setIsToggle(bool toggle) { return mIsToggle = toggle; } + protected: - - virtual void drawBorder(const LLColor4& color, S32 size); - - void setImageUnselectedID(const LLUUID &image_id); - const LLUUID& getImageUnselectedID() const { return mImageUnselectedID; } - void setImageSelectedID(const LLUUID &image_id); - const LLUUID& getImageSelectedID() const { return mImageSelectedID; } - void setImageHoverSelectedID(const LLUUID &image_id); - void setImageHoverUnselectedID(const LLUUID &image_id); - void setImageDisabledID(const LLUUID &image_id); - void setImageDisabledSelectedID(const LLUUID &image_id); const LLPointer& getImageUnselected() const { return mImageUnselected; } const LLPointer& getImageSelected() const { return mImageSelected; } - + void getOverlayImageSize(S32& overlay_width, S32& overlay_height); + LLFrameTimer mMouseDownTimer; + bool mNeedsHighlight; + S32 mButtonFlashCount; + F32 mButtonFlashRate; -private: - - void (*mClickedCallback)(void* data ); - void (*mMouseDownCallback)(void *data); - void (*mMouseUpCallback)(void *data); - void (*mHeldDownCallback)(void *data); + void drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size); + void resetMouseDownTimer(); + commit_signal_t* mMouseDownSignal; + commit_signal_t* mMouseUpSignal; + commit_signal_t* mHeldDownSignal; + const LLFontGL *mGLFont; - S32 mMouseDownFrame; - F32 mHeldDownDelay; // seconds, after which held-down callbacks get called - S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called + S32 mMouseDownFrame; + S32 mMouseHeldDownCount; // Counter for parameter passed to held-down callback + F32 mHeldDownDelay; // seconds, after which held-down callbacks get called + S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called LLPointer mImageOverlay; - LLFontGL::HAlign mImageOverlayAlignment; - LLColor4 mImageOverlayColor; + LLFontGL::HAlign mImageOverlayAlignment; + LLUIColor mImageOverlayColor; + LLUIColor mImageOverlaySelectedColor; + LLUIColor mImageOverlayDisabledColor; - LLPointer mImageUnselected; - LLUIString mUnselectedLabel; - LLColor4 mUnselectedLabelColor; + LLPointer mImageUnselected; + LLUIString mUnselectedLabel; + LLUIColor mUnselectedLabelColor; - LLPointer mImageSelected; - LLUIString mSelectedLabel; - LLColor4 mSelectedLabelColor; + LLPointer mImageSelected; + LLUIString mSelectedLabel; + LLUIColor mSelectedLabelColor; - LLPointer mImageHoverSelected; + LLPointer mImageHoverSelected; - LLPointer mImageHoverUnselected; + LLPointer mImageHoverUnselected; - LLPointer mImageDisabled; - LLUIString mDisabledLabel; - LLColor4 mDisabledLabelColor; + LLPointer mImageDisabled; + LLUIColor mDisabledLabelColor; - LLPointer mImageDisabledSelected; - LLUIString mDisabledSelectedLabel; - LLColor4 mDisabledSelectedLabelColor; + LLPointer mImageDisabledSelected; + LLUIString mDisabledSelectedLabel; + LLUIColor mDisabledSelectedLabelColor; - LLUUID mImageUnselectedID; - LLUUID mImageSelectedID; - LLUUID mImageHoverSelectedID; - LLUUID mImageHoverUnselectedID; - LLUUID mImageDisabledID; - LLUUID mImageDisabledSelectedID; - std::string mImageUnselectedName; - std::string mImageSelectedName; - std::string mImageHoverSelectedName; - std::string mImageHoverUnselectedName; - std::string mImageDisabledName; - std::string mImageDisabledSelectedName; + LLPointer mImagePressed; + LLPointer mImagePressedSelected; - LLColor4 mHighlightColor; - LLColor4 mUnselectedBgColor; - LLColor4 mSelectedBgColor; - LLColor4 mFlashBgColor; + /* There are two ways an image can flash- by making changes in color according to flash_color attribute + or by changing icon from current to the one specified in image_flash. Second way is used only if + flash icon name is set in attributes(by default it isn't). First way is used otherwise. */ + LLPointer mImageFlash; - LLColor4 mImageColor; - LLColor4 mDisabledImageColor; + LLUIColor mFlashBgColor; - BOOL mIsToggle; - BOOL mToggleState; - BOOL mScaleImage; + LLUIColor mImageColor; + LLUIColor mDisabledImageColor; - BOOL mDropShadowedText; + bool mIsToggle; + bool mScaleImage; - BOOL mBorderEnabled; + bool mDropShadowedText; + bool mAutoResize; + bool mBorderEnabled; + bool mFlashing; - BOOL mFlashing; + LLFontGL::HAlign mHAlign; + S32 mLeftHPad; + S32 mRightHPad; + S32 mBottomVPad; // under text label - LLFontGL::HAlign mHAlign; - S32 mLeftHPad; - S32 mRightHPad; + S32 mImageOverlayTopPad; + S32 mImageOverlayBottomPad; - F32 mHoverGlowStrength; - F32 mCurGlowStrength; + /* + * Space between image_overlay and label + */ + S32 mImgOverlayLabelSpace; - BOOL mNeedsHighlight; - BOOL mCommitOnReturn; + F32 mHoverGlowStrength; + F32 mCurGlowStrength; - std::string mHelpURL; + bool mCommitOnReturn; + bool mFadeWhenDisabled; - LLPointer mImagep; + bool mForcePressedState; + bool mDisplayPressedState; - LLFrameTimer mFlashingTimer; + std::string mHelpURL; + + LLFrameTimer mFlashingTimer; + + bool mHandleRightMouse; + + F32 mAlpha; }; #endif // LL_LLBUTTON_H diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index c1daee798..c7ed9ba34 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -113,37 +113,27 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const std::string& name, const LLRect& rect, LLCHECKBOXCTRL_VPAD, LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING + text_width + LLCHECKBOXCTRL_HPAD, llmax( text_height, LLCHECKBOXCTRL_BTN_SIZE ) + LLCHECKBOXCTRL_VPAD); - std::string active_true_id, active_false_id; - std::string inactive_true_id, inactive_false_id; - if (mRadioStyle) - { - active_true_id = "UIImgRadioActiveSelectedUUID"; - active_false_id = "UIImgRadioActiveUUID"; - inactive_true_id = "UIImgRadioInactiveSelectedUUID"; - inactive_false_id = "UIImgRadioInactiveUUID"; - mButton = new LLButton(std::string("Radio control button"), btn_rect, - active_false_id, active_true_id, control_which, - &LLCheckBoxCtrl::onButtonPress, this, LLFontGL::getFontSansSerif() ); - mButton->setDisabledImages( inactive_false_id, inactive_true_id ); - mButton->setHoverGlowStrength(0.35f); - } - else - { - active_false_id = "UIImgCheckboxActiveUUID"; - active_true_id = "UIImgCheckboxActiveSelectedUUID"; - inactive_true_id = "UIImgCheckboxInactiveSelectedUUID"; - inactive_false_id = "UIImgCheckboxInactiveUUID"; - mButton = new LLButton(std::string("Checkbox control button"), btn_rect, - active_false_id, active_true_id, control_which, - &LLCheckBoxCtrl::onButtonPress, this, LLFontGL::getFontSansSerif() ); - mButton->setDisabledImages( inactive_false_id, inactive_true_id ); - mButton->setHoverGlowStrength(0.35f); - } + + std::string active_true_id = mRadioStyle ? "UIImgRadioActiveSelectedUUID" : "UIImgCheckboxActiveSelectedUUID"; + std::string active_false_id = mRadioStyle ? "UIImgRadioActiveUUID" : "UIImgCheckboxActiveUUID"; + std::string inactive_true_id = mRadioStyle ? "UIImgRadioInactiveSelectedUUID" : "UIImgCheckboxInactiveSelectedUUID"; + std::string inactive_false_id = mRadioStyle ? "UIImgRadioInactiveUUID" : "UIImgCheckboxInactiveUUID"; + std::string button_name = mRadioStyle ? "Radio control button" : "Checkbox control button"; + + mButton = new LLButton( button_name, btn_rect, + active_false_id, active_true_id, control_which, + &LLCheckBoxCtrl::onButtonPress, this, LLFontGL::getFontSansSerif() ); + + mButton->setImageDisabledSelected(LLUI::getUIImage(inactive_true_id)); + mButton->setImageDisabled(LLUI::getUIImage(inactive_false_id)); + mButton->setHoverGlowStrength(0.35f); + mButton->setIsToggle(TRUE); mButton->setToggleState( initial_value ); mButton->setFollowsLeft(); mButton->setFollowsBottom(); mButton->setCommitOnReturn(FALSE); + mButton->setScaleImage(FALSE); addChild(mButton); } @@ -156,22 +146,8 @@ LLCheckBoxCtrl::~LLCheckBoxCtrl() // static void LLCheckBoxCtrl::onButtonPress( void *userdata ) { - LLCheckBoxCtrl* self = (LLCheckBoxCtrl*) userdata; - - if (self->mRadioStyle) - { - self->setValue(TRUE); - } - - self->setControlValue(self->getValue()); - // HACK: because buttons don't normally commit + LLCheckBoxCtrl* self = (LLCheckBoxCtrl*)userdata; self->onCommit(); - - if (self->mKeyboardFocusOnClick) - { - self->setFocus( TRUE ); - self->onFocusReceived(); - } } void LLCheckBoxCtrl::onCommit() @@ -179,6 +155,7 @@ void LLCheckBoxCtrl::onCommit() if( getEnabled() ) { setTentative(FALSE); + setControlValue(getValue()); LLUICtrl::onCommit(); } } @@ -186,7 +163,15 @@ void LLCheckBoxCtrl::onCommit() void LLCheckBoxCtrl::setEnabled(BOOL b) { LLView::setEnabled(b); - mButton->setEnabled(b); + + if (b) + { + mLabel->setColor( mTextEnabledColor.get() ); + } + else + { + mLabel->setColor( mTextDisabledColor.get() ); + } } void LLCheckBoxCtrl::clear() @@ -219,21 +204,6 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) LLUICtrl::reshape(width, height, called_from_parent); } -void LLCheckBoxCtrl::draw() -{ - if (getEnabled()) - { - mLabel->setColor( mTextEnabledColor ); - } - else - { - mLabel->setColor( mTextDisabledColor ); - } - - // Draw children - LLUICtrl::draw(); -} - //virtual void LLCheckBoxCtrl::setValue(const LLSD& value ) { @@ -246,6 +216,18 @@ LLSD LLCheckBoxCtrl::getValue() const return mButton->getValue(); } +//virtual +void LLCheckBoxCtrl::setTentative(BOOL b) +{ + mButton->setTentative(b); +} + +//virtual +BOOL LLCheckBoxCtrl::getTentative() const +{ + return mButton->getTentative(); +} + void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label ) { mLabel->setText( label ); @@ -282,7 +264,7 @@ BOOL LLCheckBoxCtrl::isDirty() const { if ( mButton ) { - return (mSetValue != mButton->getToggleState()); + return mButton->isDirty(); } return FALSE; // Shouldn't get here } @@ -293,11 +275,12 @@ void LLCheckBoxCtrl::resetDirty() { if ( mButton ) { - mSetValue = mButton->getToggleState(); + mButton->resetDirty(); } } + // virtual LLXMLNodePtr LLCheckBoxCtrl::getXML(bool save_children) const { diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h index ff867f519..b9f8bec6f 100644 --- a/indra/llui/llcheckboxctrl.h +++ b/indra/llui/llcheckboxctrl.h @@ -82,7 +82,6 @@ public: virtual void setEnabled( BOOL b ); - virtual void draw(); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); // LLUICtrl interface @@ -91,8 +90,8 @@ public: BOOL get() { return (BOOL)getValue().asBoolean(); } void set(BOOL value) { setValue(value); } - virtual void setTentative(BOOL b) { mButton->setTentative(b); } - virtual BOOL getTentative() const { return mButton->getTentative(); } + virtual void setTentative(BOOL b); + virtual BOOL getTentative() const; virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); @@ -108,6 +107,9 @@ public: void setLabel( const LLStringExplicit& label ); std::string getLabel() const; + void setFont( const LLFontGL* font ) { mFont = font; } + const LLFontGL* getFont() { return mFont; } + virtual void setControlName(const std::string& control_name, LLView* context); virtual std::string getControlName() const; @@ -121,8 +123,9 @@ protected: LLButton* mButton; LLTextBox* mLabel; const LLFontGL* mFont; - LLColor4 mTextEnabledColor; - LLColor4 mTextDisabledColor; + + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; BOOL mRadioStyle; BOOL mInitialValue; // Value set in constructor BOOL mSetValue; // Value set programmatically diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 507414958..b1b8f42e4 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -85,13 +85,13 @@ LLComboBox::LLComboBox( const std::string& name, const LLRect &rect, const std:: LLRect(), LLStringUtil::null, NULL, this); - mButton->setImageUnselected(std::string("square_btn_32x128.tga")); - mButton->setImageSelected(std::string("square_btn_selected_32x128.tga")); - mButton->setImageDisabled(std::string("square_btn_32x128.tga")); - mButton->setImageDisabledSelected(std::string("square_btn_selected_32x128.tga")); + mButton->setImageUnselected(LLUI::getUIImage("square_btn_32x128.tga")); + mButton->setImageSelected(LLUI::getUIImage("square_btn_selected_32x128.tga")); + mButton->setImageDisabled(LLUI::getUIImage("square_btn_32x128.tga")); + mButton->setImageDisabledSelected(LLUI::getUIImage("square_btn_selected_32x128.tga")); mButton->setScaleImage(TRUE); - mButton->setMouseDownCallback(onButtonDown); + mButton->setMouseDownCallback(boost::bind(&LLComboBox::onButtonDown,this)); mButton->setFont(LLFontGL::getFontSansSerifSmall()); mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); mButton->setHAlign( LLFontGL::LEFT ); @@ -110,13 +110,11 @@ LLComboBox::LLComboBox( const std::string& name, const LLRect &rect, const std:: mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT); updateLayout(); + + mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this)); } -LLComboBox::~LLComboBox() -{ - // children automatically deleted, including mMenu, mButton -} // virtual LLXMLNodePtr LLComboBox::getXML(bool save_children) const @@ -228,6 +226,16 @@ void LLComboBox::setEnabled(BOOL enabled) mButton->setEnabled(enabled); } + +LLComboBox::~LLComboBox() +{ + // children automatically deleted, including mMenu, mButton + + // explicitly disconect this signal, since base class destructor might fire top lost + mTopLostSignalConnection.disconnect(); +} + + void LLComboBox::clear() { if (mTextEntry) @@ -236,8 +244,6 @@ void LLComboBox::clear() } mButton->setLabelSelected(LLStringUtil::null); mButton->setLabelUnselected(LLStringUtil::null); - mButton->setDisabledLabel(LLStringUtil::null); - mButton->setDisabledSelectedLabel(LLStringUtil::null); mList->deselectAllItems(); } @@ -437,8 +443,6 @@ void LLComboBox::setLabel(const LLStringExplicit& name) { mButton->setLabelUnselected(name); mButton->setLabelSelected(name); - mButton->setDisabledLabel(name); - mButton->setDisabledSelectedLabel(name); } } @@ -481,12 +485,6 @@ void LLComboBox::onFocusLost() LLUICtrl::onFocusLost(); } -void LLComboBox::onLostTop() -{ - hideList(); -} - - void LLComboBox::setButtonVisible(BOOL visible) { mButton->setVisible(visible); @@ -749,10 +747,10 @@ void LLComboBox::onButtonDown(void *userdata) self->setFocus( TRUE ); // pass mouse capture on to list if button is depressed - if (self->mButton->hasMouseCapture()) + /*if (self->mButton->hasMouseCapture()) { gFocusMgr.setMouseCapture(self->mList); - } + }*/ } else { @@ -1216,7 +1214,7 @@ LLFlyoutButton::LLFlyoutButton( LLRect(), LLStringUtil::null, NULL, this); mActionButton->setScaleImage(TRUE); - mActionButton->setClickedCallback(onActionButtonClick); + mActionButton->setClickedCallback(boost::bind(&LLFlyoutButton::onActionButtonClick, this)); mActionButton->setFollowsAll(); mActionButton->setHAlign( LLFontGL::HCENTER ); mActionButton->setLabel(label); diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 3ed035187..a1be4cab3 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -80,7 +80,6 @@ public: virtual void draw(); virtual void onFocusLost(); - virtual void onLostTop(); virtual void setEnabled(BOOL enabled); @@ -212,6 +211,7 @@ private: bool mSuppressTentative; void (*mPrearrangeCallback)(LLUICtrl*,void*); void (*mTextEntryCallback)(LLLineEditor*, void*); + boost::signals2::connection mTopLostSignalConnection; }; class LLFlyoutButton : public LLComboBox diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 08924ac11..114cabc0c 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -430,6 +430,27 @@ void LLFloater::initFloater(const std::string& title, } } +void LLFloater::enableResizeCtrls(bool enable, bool width, bool height) +{ + mResizeBar[LLResizeBar::LEFT]->setVisible(enable && width); + mResizeBar[LLResizeBar::LEFT]->setEnabled(enable && width); + + mResizeBar[LLResizeBar::TOP]->setVisible(enable && height); + mResizeBar[LLResizeBar::TOP]->setEnabled(enable && height); + + mResizeBar[LLResizeBar::RIGHT]->setVisible(enable && width); + mResizeBar[LLResizeBar::RIGHT]->setEnabled(enable && width); + + mResizeBar[LLResizeBar::BOTTOM]->setVisible(enable && height); + mResizeBar[LLResizeBar::BOTTOM]->setEnabled(enable && height); + + for (S32 i = 0; i < 4; ++i) + { + mResizeHandle[i]->setVisible(enable && width && height); + mResizeHandle[i]->setEnabled(enable && width && height); + } +} + // virtual LLFloater::~LLFloater() { @@ -707,6 +728,8 @@ const std::string& LLFloater::getCurrentTitle() const void LLFloater::setTitle( const std::string& title ) { + if(mTitle == title) + return; mTitle = title; applyTitle(); } @@ -1542,10 +1565,12 @@ void LLFloater::setCanResize(BOOL can_resize) { for (S32 i = 0; i < 4; i++) { - removeChild(mResizeBar[i], TRUE); + removeChild(mResizeBar[i]); + delete mResizeBar[i]; mResizeBar[i] = NULL; - removeChild(mResizeHandle[i], TRUE); + removeChild(mResizeHandle[i]); + delete mResizeHandle[i]; mResizeHandle[i] = NULL; } } @@ -1611,6 +1636,7 @@ void LLFloater::setCanResize(BOOL can_resize) mMinHeight, LLResizeHandle::LEFT_TOP ); addChild(mResizeHandle[3]); + enableResizeCtrls(can_resize); } mResizable = can_resize; } @@ -1714,8 +1740,8 @@ void LLFloater::buildButtons() buttonp->setFollowsRight(); buttonp->setToolTip( sButtonToolTips[i] ); buttonp->setImageColor(LLUI::sColorsGroup->getColor("FloaterButtonImageColor")); - buttonp->setHoverImages(sButtonPressedImageNames[i], - sButtonPressedImageNames[i]); + buttonp->setImageHoverSelected(LLUI::getUIImage(sButtonPressedImageNames[i])); + buttonp->setImageHoverUnselected(LLUI::getUIImage(sButtonPressedImageNames[i])); buttonp->setScaleImage(TRUE); buttonp->setSaveToXML(false); addChild(buttonp); @@ -2459,8 +2485,12 @@ void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list) 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) + // make a copy of the list since some floaters change their + // order in the childList when changing visibility. + child_list_t child_list_copy = *getChildList(); + + for (child_list_const_iter_t child_iter = child_list_copy.begin(); + child_iter != child_list_copy.end(); ++child_iter) { LLView *view = *child_iter; if (skip_list.find(view) == skip_list.end()) diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 939071b0f..b61a567bb 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -250,6 +250,7 @@ public: static BOOL getEditModeEnabled() { return sEditModeEnabled; } static LLMultiFloater* getFloaterHost() {return sHostp; } + void enableResizeCtrls(bool enable, bool width = true, bool height = true); protected: virtual void bringToFront(S32 x, S32 y); diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 96b01b98f..66834a6a1 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -2,31 +2,25 @@ * @file llfocusmgr.cpp * @brief LLFocusMgr base class * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,13 +32,11 @@ const F32 FOCUS_FADE_TIME = 0.3f; -// NOTE: the LLFocusableElement implementation has been here from lluictrl.cpp. - LLFocusableElement::LLFocusableElement() : mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), - mFocusCallbackUserData(NULL) + mTopLostCallback(NULL) { } @@ -63,31 +55,27 @@ BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa // virtual LLFocusableElement::~LLFocusableElement() { + delete mFocusLostCallback; + delete mFocusReceivedCallback; + delete mFocusChangedCallback; + delete mTopLostCallback; } void LLFocusableElement::onFocusReceived() { - if( mFocusReceivedCallback ) - { - mFocusReceivedCallback( this, mFocusCallbackUserData ); - } - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mFocusCallbackUserData ); - } + if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this); + if (mFocusChangedCallback) (*mFocusChangedCallback)(this); } void LLFocusableElement::onFocusLost() { - if( mFocusLostCallback ) - { - mFocusLostCallback( this, mFocusCallbackUserData ); - } + if (mFocusLostCallback) (*mFocusLostCallback)(this); + if (mFocusChangedCallback) (*mFocusChangedCallback)(this); +} - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mFocusCallbackUserData ); - } +void LLFocusableElement::onTopLost() +{ + if (mTopLostCallback) (*mTopLostCallback)(this); } BOOL LLFocusableElement::hasFocus() const @@ -99,28 +87,63 @@ void LLFocusableElement::setFocus(BOOL b) { } +boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb) +{ + if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t(); + return mFocusLostCallback->connect(cb); +} + +boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) +{ + if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t(); + return mFocusReceivedCallback->connect(cb); +} + +boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb) +{ + if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t(); + return mFocusChangedCallback->connect(cb); +} + +boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb) +{ + if (!mTopLostCallback) mTopLostCallback = new focus_signal_t(); + return mTopLostCallback->connect(cb); +} + + + +typedef std::list > view_handle_list_t; +typedef std::map, LLHandle > focus_history_map_t; +struct LLFocusMgr::Impl +{ + // caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost + view_handle_list_t mCachedKeyboardFocusList; + + focus_history_map_t mFocusHistory; +}; LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() - : - mLockedView( NULL ), +: mLockedView( NULL ), mMouseCaptor( NULL ), mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), mKeystrokesOnly(FALSE), mTopCtrl( NULL ), - mFocusWeight(0.f), - mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true - #ifdef _DEBUG - , mMouseCaptorName("none") - , mKeyboardFocusName("none") - , mTopCtrlName("none") - #endif + mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true + mImpl(new LLFocusMgr::Impl) { } +LLFocusMgr::~LLFocusMgr() +{ + mImpl->mFocusHistory.clear(); + delete mImpl; + mImpl = NULL; +} void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) { @@ -151,6 +174,12 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) { + // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived) + // making the rest of our processing unnecessary since it will already be + // handled by the recursive call + static bool focus_dirty; + focus_dirty = false; + if (mLockedView && (new_focus == NULL || (new_focus != mLockedView @@ -162,8 +191,6 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL return; } - //llinfos << "Keyboard focus handled by " << (new_focus ? new_focus->getName() : "nothing") << llendl; - mKeystrokesOnly = keystrokes_only; if( new_focus != mKeyboardFocus ) @@ -171,23 +198,58 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; - if( mLastKeyboardFocus ) + // list of the focus and it's ancestors + view_handle_list_t old_focus_list = mImpl->mCachedKeyboardFocusList; + view_handle_list_t new_focus_list; + + // walk up the tree to root and add all views to the new_focus_list + for (LLView* ctrl = dynamic_cast(mKeyboardFocus); ctrl; ctrl = ctrl->getParent()) { - mLastKeyboardFocus->onFocusLost(); + new_focus_list.push_back(ctrl->getHandle()); } - // clear out any existing flash - if (new_focus) + // remove all common ancestors since their focus is unchanged + while (!new_focus_list.empty() && + !old_focus_list.empty() && + new_focus_list.back() == old_focus_list.back()) { - mFocusWeight = 0.f; - new_focus->onFocusReceived(); + new_focus_list.pop_back(); + old_focus_list.pop_back(); } - mFocusTimer.reset(); - #ifdef _DEBUG - LLUICtrl* focus_ctrl = dynamic_cast(new_focus); - mKeyboardFocusName = focus_ctrl ? focus_ctrl->getName() : std::string("none"); - #endif + // walk up the old focus branch calling onFocusLost + // we bubble up the tree to release focus, and back down to add + for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin(); + old_focus_iter != old_focus_list.end() && !focus_dirty; + old_focus_iter++) + { + LLView* old_focus_view = old_focus_iter->get(); + if (old_focus_view) + { + mImpl->mCachedKeyboardFocusList.pop_front(); + old_focus_view->onFocusLost(); + } + } + + // walk down the new focus branch calling onFocusReceived + for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin(); + new_focus_riter != new_focus_list.rend() && !focus_dirty; + new_focus_riter++) + { + LLView* new_focus_view = new_focus_riter->get(); + if (new_focus_view) + { + mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle()); + new_focus_view->onFocusReceived(); + } + } + + // if focus was changed as part of an onFocusLost or onFocusReceived call + // stop iterating on current list since it is now invalid + if (focus_dirty) + { + return; + } // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. @@ -212,7 +274,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL if (focus_subtree) { LLView* focused_view = dynamic_cast(mKeyboardFocus); - mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle(); + mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle(); } } @@ -220,6 +282,8 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL { lockFocus(); } + + focus_dirty = true; } @@ -241,7 +305,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const // Returns TRUE is parent or any descedent of parent is the mouse captor. BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { - if( mMouseCaptor && mMouseCaptor->isView() ) + if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) @@ -268,65 +332,42 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f if( mKeyboardFocus == focus ) { mKeyboardFocus = NULL; - #ifdef _DEBUG - mKeyboardFocusName = std::string("none"); - #endif } } void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) { - //if (mFocusLocked) - //{ - // return; - //} - if( new_captor != mMouseCaptor ) { LLMouseHandler* old_captor = mMouseCaptor; mMouseCaptor = new_captor; - /* - if (new_captor) + + if (LLView::sDebugMouseHandling) { - if ( new_captor->getName() == "Stickto") + if (new_captor) { llinfos << "New mouse captor: " << new_captor->getName() << llendl; } else { - llinfos << "New mouse captor: " << new_captor->getName() << llendl; + llinfos << "New mouse captor: NULL" << llendl; } } - else - { - llinfos << "New mouse captor: NULL" << llendl; - } - */ - + if( old_captor ) { old_captor->onMouseCaptureLost(); } - #ifdef _DEBUG - mMouseCaptorName = new_captor ? new_captor->getName() : std::string("none"); - #endif } } void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ) { - //if (mFocusLocked) - //{ - // return; - //} if( mMouseCaptor == captor ) { mMouseCaptor = NULL; - #ifdef _DEBUG - mMouseCaptorName = std::string("none"); - #endif } } @@ -355,13 +396,9 @@ void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { mTopCtrl = new_top; - #ifdef _DEBUG - mTopCtrlName = new_top ? new_top->getName() : std::string("none"); - #endif - if (old_top) { - old_top->onLostTop(); + old_top->onTopLost(); } } } @@ -371,9 +408,6 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) if( mTopCtrl == top_view ) { mTopCtrl = NULL; - #ifdef _DEBUG - mTopCtrlName = std::string("none"); - #endif } } @@ -389,12 +423,13 @@ void LLFocusMgr::unlockFocus() F32 LLFocusMgr::getFocusFlashAmt() const { - return clamp_rescale(getFocusTime(), 0.f, FOCUS_FADE_TIME, mFocusWeight, 0.f); + return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f); } LLColor4 LLFocusMgr::getFocusColor() const { - LLColor4 focus_color = lerp(LLUI::sColorsGroup->getColor( "FocusColor" ), LLColor4::white, getFocusFlashAmt()); + static LLCachedControl focus_color_cached(*LLUI::sColorsGroup,"FocusColor", LLColor4::white); + LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt()); // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem) if (!mAppHasFocus) { @@ -405,8 +440,7 @@ LLColor4 LLFocusMgr::getFocusColor() const void LLFocusMgr::triggerFocusFlash() { - mFocusTimer.reset(); - mFocusWeight = 1.f; + mFocusFlashTimer.reset(); } void LLFocusMgr::setAppHasFocus(BOOL focus) @@ -428,8 +462,8 @@ LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const { if (subtree_root) { - focus_history_map_t::const_iterator found_it = mFocusHistory.find(subtree_root->getHandle()); - if (found_it != mFocusHistory.end()) + focus_history_map_t::const_iterator found_it = mImpl->mFocusHistory.find(subtree_root->getHandle()); + if (found_it != mImpl->mFocusHistory.end()) { // found last focus for this subtree return static_cast(found_it->second.get()); @@ -442,6 +476,6 @@ void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) { if (subtree_root) { - mFocusHistory.erase(subtree_root->getHandle()); + mImpl->mFocusHistory.erase(subtree_root->getHandle()); } } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index ce0c75063..14c711b5e 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -2,31 +2,25 @@ * @file llfocusmgr.h * @brief LLFocusMgr base class * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,12 +32,12 @@ #include "llstring.h" #include "llframetimer.h" #include "llui.h" -#include "llhandle.h" class LLUICtrl; class LLMouseHandler; class LLView; +// NOTE: the LLFocusableElement class declaration has been moved here from lluictrl.h. class LLFocusableElement { friend class LLFocusMgr; // allow access to focus change handlers @@ -54,21 +48,25 @@ public: virtual void setFocus( BOOL b ); virtual BOOL hasFocus() const; - void setFocusLostCallback(void (*cb)(LLFocusableElement* caller, void*), void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; } - void setFocusReceivedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; } - void setFocusChangedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; } + typedef boost::signals2::signal focus_signal_t; + + boost::signals2::connection setFocusLostCallback( const focus_signal_t::slot_type& cb); + boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb); + boost::signals2::connection setFocusChangedCallback(const focus_signal_t::slot_type& cb); + boost::signals2::connection setTopLostCallback(const focus_signal_t::slot_type& cb); // These were brought up the hierarchy from LLView so that we don't have to use dynamic_cast when dealing with keyboard focus. virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere protected: virtual void onFocusReceived(); virtual void onFocusLost(); - void (*mFocusLostCallback)( LLFocusableElement* caller, void* userdata ); - void (*mFocusReceivedCallback)( LLFocusableElement* ctrl, void* userdata ); - void (*mFocusChangedCallback)( LLFocusableElement* ctrl, void* userdata ); - void* mFocusCallbackUserData; + focus_signal_t* mFocusLostCallback; + focus_signal_t* mFocusReceivedCallback; + focus_signal_t* mFocusChangedCallback; + focus_signal_t* mTopLostCallback; }; @@ -76,7 +74,7 @@ class LLFocusMgr { public: LLFocusMgr(); - ~LLFocusMgr() { mFocusHistory.clear(); } + ~LLFocusMgr(); // Mouse Captor void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse. @@ -93,7 +91,7 @@ public: BOOL getKeystrokesOnly() { return mKeystrokesOnly; } void setKeystrokesOnly(BOOL keystrokes_only) { mKeystrokesOnly = keystrokes_only; } - F32 getFocusTime() const { return mFocusTimer.getElapsedTimeF32(); } + F32 getFocusTime() const { return mFocusFlashTimer.getElapsedTimeF32(); } F32 getFocusFlashAmt() const; S32 getFocusFlashWidth() const { return llround(lerp(1.f, 3.f, getFocusFlashAmt())); } LLColor4 getFocusColor() const; @@ -121,6 +119,9 @@ public: void unlockFocus(); BOOL focusLocked() const { return mLockedView != NULL; } + + struct Impl; + private: LLUICtrl* mLockedView; @@ -132,23 +133,15 @@ private: LLFocusableElement* mLastKeyboardFocus; // who last had focus LLFocusableElement* mDefaultKeyboardFocus; BOOL mKeystrokesOnly; - + // Top View LLUICtrl* mTopCtrl; - LLFrameTimer mFocusTimer; - F32 mFocusWeight; + LLFrameTimer mFocusFlashTimer; BOOL mAppHasFocus; - typedef std::map, LLHandle > focus_history_map_t; - focus_history_map_t mFocusHistory; - - #ifdef _DEBUG - std::string mMouseCaptorName; - std::string mKeyboardFocusName; - std::string mTopCtrlName; - #endif + Impl * mImpl; }; extern LLFocusMgr gFocusMgr; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 60f8f9934..f54d2c761 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -104,7 +104,6 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect, : LLUICtrl( name, rect, TRUE, commit_callback, userdata, FOLLOWS_TOP | FOLLOWS_LEFT ), mMaxLengthBytes(max_length_bytes), - mPopupMenuHandle(), mCursorPos( 0 ), mScrollHPos( 0 ), mTextPadLeft(0), @@ -140,7 +139,8 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect, mHaveHistory(FALSE), mImage( sImage ), mReplaceNewlinesWithSpaces( TRUE ), - mSpellCheckable( FALSE ) + mSpellCheckable( FALSE ), + mContextMenuHandle() { llassert( max_length_bytes > 0 ); @@ -156,7 +156,8 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect, mGLFont = LLFontGL::getFontSansSerifSmall(); } - setFocusLostCallback(focus_lost_callback); + if(focus_lost_callback) + setFocusLostCallback(boost::bind(focus_lost_callback,_1,(void*)NULL)); setTextPadding(0, 0); @@ -185,35 +186,30 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect, { menu = new LLMenuGL(LLStringUtil::null); }*/ - menu->append(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); - menu->append(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); - menu->append(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); - menu->append(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); - menu->append(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); - menu->appendSeparator("Spelsep"); + menu->addChild(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); + menu->addSeparator(); //menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor")); menu->setCanTearOff(FALSE); menu->setVisible(FALSE); - mPopupMenuHandle = menu->getHandle(); + setContextMenu(menu); } - - + LLLineEditor::~LLLineEditor() { mCommitOnFocusLost = FALSE; + // calls onCommit() while LLLineEditor still valid gFocusMgr.releaseFocusIfNeeded( this ); - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - LLView::deleteViewByHandle(mPopupMenuHandle); } void LLLineEditor::onFocusReceived() { + gEditMenuHandler = this; LLUICtrl::onFocusReceived(); updateAllowingLanguageInput(); } @@ -645,105 +641,6 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) return TRUE; } - -BOOL LLLineEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - setFocus(TRUE); - - //setCursorAtLocalPos( x); - S32 wordStart = 0; - S32 wordLen = 0; - S32 pos = calculateCursorFromMouse(x); - - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu) - { - if(menu->isOpen()) - { - menu->setVisible(FALSE); - } - for (int i = 0;i<(int)suggestionMenuItems.size();i++) - { - SpellMenuBind * tempBind = suggestionMenuItems[i]; - if(tempBind) - { - menu->remove((LLMenuItemCallGL *)tempBind->menuItem); - ((LLMenuItemCallGL *)tempBind->menuItem)->die(); - //delete tempBind->menuItem; - //tempBind->menuItem = NULL; - delete tempBind; - } - } - suggestionMenuItems.clear(); - - // spell_check="true" in xui - menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable); - if (!mReadOnly && mSpellCheckable) - { - // search for word matches - bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen); - if (is_word_part) - { - const LLWString& text = mText.getWString(); - std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen)); - - if (!glggHunSpell->isSpelledRight(selectedWord)) - { - //misspelled word here, and you have just right clicked on it! - std::vector suggs = glggHunSpell->getSuggestionList(selectedWord); - - for (int i = 0; i<(int)suggs.size() ;i++) - { - SpellMenuBind * tempStruct = new SpellMenuBind; - tempStruct->origin = this; - tempStruct->word = suggs[i]; - tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( - tempStruct->word, spell_correct, NULL, tempStruct); - //new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); - tempStruct->menuItem = suggMenuItem; - suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); - } - SpellMenuBind * tempStruct = new SpellMenuBind; - tempStruct->origin = this; - tempStruct->word = selectedWord; - tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( - "Add Word", spell_add, NULL, tempStruct); - tempStruct->menuItem = suggMenuItem; - suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); - } - } - - SpellMenuBind * tempStruct = new SpellMenuBind; - tempStruct->origin = this; - if (glggHunSpell->getSpellCheckHighlight()) - { - tempStruct->word = "Hide Misspellings"; - } - else - { - tempStruct->word = "Show Misspellings"; - } - LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( - tempStruct->word, spell_show, NULL, tempStruct); - tempStruct->menuItem = suggMenuItem; - suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); - } - - mLastContextMenuX = x; - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - } - return TRUE; -} - BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) { // Check first whether the "clear search" button wants to deal with this. @@ -835,6 +732,16 @@ BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) return TRUE; } +BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + setFocus(TRUE); + if (!LLUICtrl::handleRightMouseDown(x, y, mask)) + { + showContextMenu(x, y); + } + return TRUE; +} + BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -1615,7 +1522,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) // SL-51858: Key presses are not being passed to the Popup menu. // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); if (menu && menu->isOpen()) { LLMenuGL::sMenuContainer->hideMenus(); @@ -1697,7 +1604,7 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) { // SL-51858: Key presses are not being passed to the Popup menu. // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); if (menu && menu->isOpen()) { LLMenuGL::sMenuContainer->hideMenus(); @@ -2060,7 +1967,7 @@ void LLLineEditor::draw() // Make sure the IME is in the right place S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position - LLRect screen_pos = getScreenRect(); + LLRect screen_pos = calcScreenRect(); LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - UI_LINEEDITOR_V_PAD ); ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); @@ -3069,6 +2976,121 @@ LLWString LLLineEditor::getConvertedText() const return text; } +void LLLineEditor::showContextMenu(S32 x, S32 y) +{ + LLMenuGL* menu = static_cast(mContextMenuHandle.get()); + + if (menu) + { + gEditMenuHandler = this; + + /*S32 screen_x, screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + menu->show(screen_x, screen_y);*/ + + + //setCursorAtLocalPos( x); + S32 wordStart = 0; + S32 wordLen = 0; + S32 pos = calculateCursorFromMouse(x); + + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); + if (menu) + { + if(menu->isOpen()) + { + menu->setVisible(FALSE); + } + for (int i = 0;i<(int)suggestionMenuItems.size();i++) + { + SpellMenuBind * tempBind = suggestionMenuItems[i]; + if(tempBind) + { + menu->removeChild((LLMenuItemCallGL *)tempBind->menuItem); + ((LLMenuItemCallGL *)tempBind->menuItem)->die(); + //delete tempBind->menuItem; + //tempBind->menuItem = NULL; + delete tempBind; + } + } + suggestionMenuItems.clear(); + + // spell_check="true" in xui + menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable); + if (!mReadOnly && mSpellCheckable) + { + // search for word matches + bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen); + if (is_word_part) + { + const LLWString& text = mText.getWString(); + std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen)); + + if (!glggHunSpell->isSpelledRight(selectedWord)) + { + //misspelled word here, and you have just right clicked on it! + std::vector suggs = glggHunSpell->getSuggestionList(selectedWord); + + for (int i = 0; i<(int)suggs.size() ;i++) + { + SpellMenuBind * tempStruct = new SpellMenuBind; + tempStruct->origin = this; + tempStruct->word = suggs[i]; + tempStruct->wordPositionEnd = wordStart + wordLen; + tempStruct->wordPositionStart=wordStart; + LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( + tempStruct->word, spell_correct, NULL, tempStruct); + //new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); + tempStruct->menuItem = suggMenuItem; + suggestionMenuItems.push_back(tempStruct); + menu->addChild(suggMenuItem); + } + SpellMenuBind * tempStruct = new SpellMenuBind; + tempStruct->origin = this; + tempStruct->word = selectedWord; + tempStruct->wordPositionEnd = wordStart + wordLen; + tempStruct->wordPositionStart=wordStart; + LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( + "Add Word", spell_add, NULL, tempStruct); + tempStruct->menuItem = suggMenuItem; + suggestionMenuItems.push_back(tempStruct); + menu->addChild(suggMenuItem); + } + } + + SpellMenuBind * tempStruct = new SpellMenuBind; + tempStruct->origin = this; + if (glggHunSpell->getSpellCheckHighlight()) + { + tempStruct->word = "Hide Misspellings"; + } + else + { + tempStruct->word = "Show Misspellings"; + } + LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( + tempStruct->word, spell_show, NULL, tempStruct); + tempStruct->menuItem = suggMenuItem; + suggestionMenuItems.push_back(tempStruct); + menu->addChild(suggMenuItem); + } + + mLastContextMenuX = x; + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + } + } +} + +void LLLineEditor::setContextMenu(LLMenuGL* new_context_menu) +{ + if (new_context_menu) + mContextMenuHandle = new_context_menu->getHandle(); + else + mContextMenuHandle.markDead(); +} + static LLRegisterWidget r2("search_editor"); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 6c6ca2884..280fafbca 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -56,6 +56,7 @@ class LLFontGL; class LLLineEditorRollback; class LLButton; +class LLMenuGL; typedef BOOL (*LLLinePrevalidateFunc)(const LLWString &wstr); @@ -79,6 +80,9 @@ public: LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE, S32 border_thickness = 1); +protected: + void showContextMenu(S32 x, S32 y); +public: virtual ~LLLineEditor(); virtual LLXMLNodePtr getXML(bool save_children = true) const; @@ -252,7 +256,9 @@ public: void updateHistory(); // stores current line in history void setReplaceNewlinesWithSpaces(BOOL replace); - + + void setContextMenu(LLMenuGL* new_context_menu); + private: // private helper methods @@ -285,7 +291,6 @@ private: virtual S32 getPreeditFontSize() const; protected: - LLHandle mPopupMenuHandle; LLUIString mText; // The string being edited. std::string mPrevText; // Saved string for 'ESC' revert LLUIString mLabel; // text label that is visible when no user text provided @@ -359,6 +364,8 @@ protected: std::vector mPreeditPositions; LLPreeditor::standouts_t mPreeditStandouts; + LLHandle mContextMenuHandle; + private: // Utility on top of LLUI::getUIImage, looks up a named image in a given XML node and returns it if possible // or returns a given default image if anything in the process fails. diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 21a6711c4..cce9eb172 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -46,20 +46,21 @@ #include "llmenugl.h" +#include "llgl.h" #include "llmath.h" #include "llrender.h" #include "llfocusmgr.h" -#include "llfont.h" #include "llcoord.h" #include "llwindow.h" #include "llcriticaldamp.h" #include "lluictrlfactory.h" +#include "llbutton.h" #include "llfontgl.h" #include "llresmgr.h" +#include "lltrans.h" #include "llui.h" -#include "lltrans.h" #include "llstl.h" #include "v2math.h" @@ -102,10 +103,10 @@ const std::string SEPARATOR_LABEL( "-----------" ); const std::string VERTICAL_SEPARATOR_LABEL( "|" ); const std::string TEAROFF_SEPARATOR_LABEL( "~~~~~~~~~~~" ); -const std::string BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK -const std::string BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE -const std::string ARROW_UP ("^^^^^^^"); -const std::string ARROW_DOWN("vvvvvvv"); +const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK +const std::string LLMenuGL::BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE +const std::string LLMenuGL::ARROW_UP ("^^^^^^^"); +const std::string LLMenuGL::ARROW_DOWN("vvvvvvv"); const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f; @@ -135,21 +136,31 @@ const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; // Default constructor LLMenuItemGL::LLMenuItemGL( const std::string& name, const std::string& label, KEY key, MASK mask ) : - LLView( name, TRUE ), + LLUICtrl( name, LLRect(), TRUE, NULL, NULL ), mJumpKey(KEY_NONE), - mAcceleratorKey( key ), - mAcceleratorMask( mask ), mAllowKeyRepeat(FALSE), mHighlight( FALSE ), mGotHover( FALSE ), mBriefItem( FALSE ), - mFont( LLFontGL::getFontSansSerif() ), mStyle(LLFontGL::NORMAL), - mDrawTextDisabled( FALSE ) + mDrawTextDisabled( FALSE ), + mFont( LLFontGL::getFontSansSerif() ), + mAcceleratorKey( key ), + mAcceleratorMask( mask ), + mLabel( label ), + mEnabledColor( sEnabledColor ), + mDisabledColor( sDisabledColor ), + mHighlightBackground( sHighlightBackground ), + mHighlightForeground( sHighlightForeground ) { setLabel( label ); } +LLMenuItemGL::~LLMenuItemGL() +{ + lldebugs << "Menu destroyed:" << this->getName() << llendl; +}; + // virtual LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const { @@ -192,6 +203,20 @@ LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const return node; } + +//virtual +void LLMenuItemGL::setValue(const LLSD& value) +{ + setLabel(value.asString()); +} + +//virtual +LLSD LLMenuItemGL::getValue() const +{ + return getLabel(); +} + +//virtual BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) @@ -209,6 +234,26 @@ BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) return TRUE; } +//virtual +BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return LLUICtrl::handleRightMouseDown(x,y,mask); +} + +//virtual +BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + // If this event came from a right-click context menu spawn, + // process as a left-click to allow menu items to be hit + if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX + || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX) + { + BOOL handled = handleMouseUp(x, y, mask); + return handled; + } + return LLUICtrl::handleRightMouseUp(x,y,mask); +} + // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) @@ -313,9 +358,20 @@ U32 LLMenuItemGL::getNominalHeight( void ) const return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; } +//virtual +void LLMenuItemGL::setBriefItem(BOOL brief) +{ + mBriefItem = brief; +} + +//virtual +BOOL LLMenuItemGL::isBriefItem() const +{ + return mBriefItem; +} // Get the parent menu for this item -LLMenuGL* LLMenuItemGL::getMenu() +LLMenuGL* LLMenuItemGL::getMenu() const { return (LLMenuGL*) getParent(); } @@ -339,7 +395,7 @@ U32 LLMenuItemGL::getNominalWidth( void ) const if( KEY_NONE != mAcceleratorKey ) { - width += ACCEL_PAD_PIXELS; + width += getMenu()->getShortcutPad(); std::string temp; appendAcceleratorString( temp ); width += mFont->getWidth( temp ); @@ -359,8 +415,10 @@ void LLMenuItemGL::buildDrawLabel( void ) void LLMenuItemGL::doIt( void ) { - // close all open menus by default - // if parent menu is actually visible (and we are not triggering menu item via accelerator) + // Check torn-off status to allow left-arrow keyboard navigation back + // to parent menu. + // Also, don't hide if item triggered by keyboard shortcut (and hence + // parent not visible). if (!getMenu()->getTornOff() && getMenu()->getVisible()) { @@ -375,6 +433,12 @@ void LLMenuItemGL::doIt( void ) { getMenu()->clearHoverItem(); } + + if (mHighlight != highlight) + { + //dirtyRect(); + } + mHighlight = highlight; } @@ -413,25 +477,30 @@ BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) return FALSE; } -BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK ) +BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) { // switch to mouse navigation mode LLMenuGL::setKeyboardMode(FALSE); doIt(); make_ui_sound("UISndClickRelease"); - return TRUE; + return LLView::handleMouseUp(x, y, mask); } -BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK ) +BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) { // switch to mouse navigation mode LLMenuGL::setKeyboardMode(FALSE); setHighlight(TRUE); - return TRUE; + return LLView::handleMouseDown(x, y, mask); } +BOOL LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) +{ + // If the menu is scrollable let it handle the wheel event. + return FALSE;//!getMenu()->isScrollable(); +} void LLMenuItemGL::draw( void ) { @@ -442,7 +511,11 @@ void LLMenuItemGL::draw( void ) // let disabled items be highlighted, just don't draw them as such if( getEnabled() && getHighlight() && !mBriefItem) { - gGL.color4fv( sHighlightBackground.mV ); + int debug_count = 0; + if (dynamic_cast(this)) + debug_count++; + gGL.color4fv( mHighlightBackground.mV ); + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } @@ -456,15 +529,15 @@ void LLMenuItemGL::draw( void ) if ( getEnabled() && getHighlight() ) { - color = sHighlightForeground; + color = mHighlightForeground; } else if( getEnabled() && !mDrawTextDisabled ) { - color = sEnabledColor; + color = mEnabledColor; } else { - color = sDisabledColor; + color = mDisabledColor; } // Draw the text on top. @@ -489,7 +562,7 @@ void LLMenuItemGL::draw( void ) } if( !mDrawBranchLabel.empty() ) { - mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, + mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, (!getEnabled() || getHighlight()) ? color : color % .7f, LLFontGL::RIGHT, LLFontGL::BOTTOM, mStyle, font_shadow, S32_MAX, S32_MAX, NULL, FALSE ); } } @@ -518,37 +591,29 @@ BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& return TRUE; } +void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility) +{ + if (getMenu()) + { + getMenu()->needsArrange(); + } + LLView::handleVisibilityChange(new_visibility); +} //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemSeparatorGL // // This class represents a separator. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLMenuItemSeparatorGL : public LLMenuItemGL -{ -public: - LLMenuItemSeparatorGL( const std::string &name = SEPARATOR_NAME ); - - virtual LLXMLNodePtr getXML(bool save_children = true) const; - - virtual std::string getType() const { return "separator"; } - - // doIt() - do the primary funcationality of the menu item. - virtual void doIt( void ) {} - - virtual void draw( void ); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - - virtual U32 getNominalHeight( void ) const { return SEPARATOR_HEIGHT_PIXELS; } -}; LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const std::string &name ) : - LLMenuItemGL( name, SEPARATOR_LABEL ) + LLMenuItemGL( name.empty() ? SEPARATOR_NAME : name, SEPARATOR_LABEL ) { } - +U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const +{ + return SEPARATOR_HEIGHT_PIXELS; +} LLXMLNodePtr LLMenuItemSeparatorGL::getXML(bool save_children) const { @@ -561,7 +626,7 @@ LLXMLNodePtr LLMenuItemSeparatorGL::getXML(bool save_children) const void LLMenuItemSeparatorGL::draw( void ) { - gGL.color4fv( getDisabledColor().mV ); + gGL.color4fv( mDisabledColor.mV ); const S32 y = getRect().getHeight() / 2; const S32 PAD = 6; gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); @@ -572,11 +637,14 @@ BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { - return parent_menu->handleMouseDown(x + getRect().mLeft, getRect().mTop + 1, mask); + // the menu items are in the child list in bottom up order + LLView* prev_menu_item = parent_menu->findNextSibling(this); + return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; } else { - return parent_menu->handleMouseDown(x + getRect().mLeft, getRect().mBottom - 1, mask); + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE; } } @@ -585,11 +653,13 @@ BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { - return parent_menu->handleMouseUp(x + getRect().mLeft, getRect().mTop + 1, mask); + LLView* prev_menu_item = parent_menu->findNextSibling(this); + return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; } else { - return parent_menu->handleMouseUp(x + getRect().mLeft, getRect().mBottom - 1, mask); + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE; } } @@ -608,7 +678,6 @@ BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) } } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemVerticalSeparatorGL // @@ -632,9 +701,8 @@ LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemTearOffGL //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLHandle parent_floater_handle) : - LLMenuItemGL(std::string("tear off"), TEAROFF_SEPARATOR_LABEL), - mParentHandle(parent_floater_handle) +LLMenuItemTearOffGL::LLMenuItemTearOffGL() : + LLMenuItemGL(std::string("tear off"), TEAROFF_SEPARATOR_LABEL) { } @@ -648,6 +716,35 @@ LLXMLNodePtr LLMenuItemTearOffGL::getXML(bool save_children) const return node; } +// Returns the first floater ancestor if there is one +LLFloater* LLMenuItemTearOffGL::getParentFloater() +{ + LLView* parent_view = getMenu(); + + while (parent_view) + { + if (dynamic_cast(parent_view)) + { + return dynamic_cast(parent_view); + } + + bool parent_is_menu = dynamic_cast(parent_view) && !dynamic_cast(parent_view); + + if (parent_is_menu) + { + // use menu parent + parent_view = dynamic_cast(parent_view)->getParentMenuItem(); + } + else + { + // just use regular view parent + parent_view = parent_view->getParent(); + } + } + + return NULL; +} + void LLMenuItemTearOffGL::doIt() { if (getMenu()->getTornOff()) @@ -663,9 +760,9 @@ void LLMenuItemTearOffGL::doIt() getMenu()->highlightNextItem(this); } - getMenu()->arrange(); + getMenu()->needsArrange(); - LLFloater* parent_floater = mParentHandle.get(); + LLFloater* parent_floater = getParentFloater(); LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); if (tear_off_menu) @@ -680,7 +777,7 @@ void LLMenuItemTearOffGL::doIt() tear_off_menu->setFocus(TRUE); } } - LLMenuItemGL::doIt(); + LLMenuItemGL::onCommit(); } void LLMenuItemTearOffGL::draw() @@ -688,17 +785,17 @@ void LLMenuItemTearOffGL::draw() // disabled items can be highlighted, but shouldn't render as such if( getEnabled() && getHighlight() && !isBriefItem()) { - gGL.color4fv( getHighlightBGColor().mV ); + gGL.color4fv( mHighlightBackground.mV ); gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } if (getEnabled()) { - gGL.color4fv( getEnabledColor().mV ); + gGL.color4fv( mEnabledColor.mV ); } else { - gGL.color4fv( getDisabledColor().mV ); + gGL.color4fv( mDisabledColor.mV ); } const S32 y = getRect().getHeight() / 3; const S32 PAD = 6; @@ -884,14 +981,21 @@ void LLMenuItemCallGL::doIt( void ) LLMenuItemGL::doIt(); } -void LLMenuItemCallGL::buildDrawLabel( void ) +void LLMenuItemCallGL::updateEnabled( void ) { - LLPointer fired_event = new LLEvent(this); - fireEvent(fired_event, "on_build"); if( mEnabledCallback ) { setEnabled( mEnabledCallback( mUserData ) ); } +} + +void LLMenuItemCallGL::buildDrawLabel( void ) +{ + updateEnabled(); + + LLPointer fired_event = new LLEvent(this); + fireEvent(fired_event, "on_build"); + if(mLabelCallback) { std::string label; @@ -901,16 +1005,18 @@ void LLMenuItemCallGL::buildDrawLabel( void ) LLMenuItemGL::buildDrawLabel(); } +BOOL LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask ) +{ + return LLMenuItemGL::handleKeyHere(key, mask); +} + BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) { if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) { LLPointer fired_event = new LLEvent(this); fireEvent(fired_event, "on_build"); - if( mEnabledCallback ) - { - setEnabled( mEnabledCallback( mUserData ) ); - } + updateEnabled(); if( !getEnabled() ) { if( mOnDisabledCallback ) @@ -971,7 +1077,7 @@ void LLMenuItemCheckGL::setValue(const LLSD& value) mChecked = value.asBoolean(); if(mChecked) { - mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; } else { @@ -1011,7 +1117,7 @@ void LLMenuItemCheckGL::buildDrawLabel( void ) { if(mChecked || (mCheckCallback && mCheckCallback( getUserData() ) ) ) { - mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; } else { @@ -1045,7 +1151,7 @@ void LLMenuItemToggleGL::buildDrawLabel( void ) { if( *mToggle ) { - mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; } else { @@ -1068,47 +1174,51 @@ void LLMenuItemToggleGL::doIt( void ) } -LLMenuItemBranchGL::LLMenuItemBranchGL( const std::string& name, const std::string& label, LLHandle branch, +LLMenuItemBranchGL::LLMenuItemBranchGL( const std::string& name, const std::string& label, LLHandle branch_handle, KEY key, MASK mask ) : - LLMenuItemGL( name, label, key, mask ), - mBranch( branch ) + LLMenuItemGL( name, label, key, mask ) { - if(!dynamic_cast(branch.get())) + LLMenuGL* branch = dynamic_cast(branch_handle.get()); + if(!branch) { llerrs << "Non-menu handle passed as branch reference." << llendl; } - if(getBranch()) + if (branch) { - getBranch()->setVisible( FALSE ); - getBranch()->setParentMenuItem(this); + mBranchHandle = branch->getHandle(); + branch->setVisible(FALSE); + branch->setParentMenuItem(this); } } LLMenuItemBranchGL::~LLMenuItemBranchGL() { - LLView::deleteViewByHandle(mBranch); + if (mBranchHandle.get()) + { + mBranchHandle.get()->die(); + } } // virtual LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const { - // richard: this is redundant with parent, remove - if (getBranch()) + LLMenuGL* branch = getBranch(); + if (branch) { - if(getBranch()->getName() == name) + if (branch->getName() == name) { - return getBranch(); + return branch; } // Always recurse on branches - LLView* child = getBranch()->getChildView(name, recurse, FALSE); + LLView* child = branch->getChildView(name, recurse, FALSE); if(child) { return child; } } - return LLView::getChildView(name, recurse, create_if_missing);; + return LLView::getChildView(name, recurse, create_if_missing); } // virtual @@ -1124,11 +1234,7 @@ BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) { - if(getBranch()) - { - return getBranch()->handleAcceleratorKey(key, mask); - } - return FALSE; + return getBranch() && getBranch()->handleAcceleratorKey(key, mask); } // virtual @@ -1147,19 +1253,21 @@ LLXMLNodePtr LLMenuItemBranchGL::getXML(bool save_children) const // if not, it will be added to the list BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) { - if(getBranch()) - { - U32 item_count = getBranch()->getItemCount(); - LLMenuItemGL *item; + LLMenuGL* branch = getBranch(); + if (!branch) + return FALSE; - while (item_count--) + U32 item_count = branch->getItemCount(); + LLMenuItemGL *item; + + while (item_count--) + { + if ((item = branch->getItem(item_count))) { - if ((item = getBranch()->getItem(item_count))) - { - return item->addToAcceleratorList(listp); - } + return item->addToAcceleratorList(listp); } } + return FALSE; } @@ -1171,7 +1279,7 @@ void LLMenuItemBranchGL::buildDrawLabel( void ) std::string st = mDrawAccelLabel; appendAcceleratorString( st ); mDrawAccelLabel = st; - mDrawBranchLabel = BRANCH_SUFFIX; + mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; } // doIt() - do the primary functionality of the menu item. @@ -1181,7 +1289,7 @@ void LLMenuItemBranchGL::doIt( void ) // keyboard navigation automatically propagates highlight to sub-menu // to facilitate fast menu control via jump keys - if (getBranch() && LLMenuGL::getKeyboardMode() && !getBranch()->getHighlightedItem()) + if (LLMenuGL::getKeyboardMode() && getBranch()&& !getBranch()->getHighlightedItem()) { getBranch()->highlightNextItem(NULL); } @@ -1190,7 +1298,7 @@ void LLMenuItemBranchGL::doIt( void ) BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) { BOOL handled = FALSE; - if (called_from_parent && getBranch()) + if (getBranch() && called_from_parent) { handled = getBranch()->handleKey(key, mask, called_from_parent); } @@ -1206,7 +1314,7 @@ BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { BOOL handled = FALSE; - if (called_from_parent && getBranch()) + if (getBranch() && called_from_parent) { handled = getBranch()->handleUnicodeChar(uni_char, TRUE); } @@ -1222,21 +1330,21 @@ BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa void LLMenuItemBranchGL::setHighlight( BOOL highlight ) { - if (highlight == getHighlight()) return; - - if(!getBranch()) - { + if (highlight == getHighlight()) return; - } - BOOL auto_open = getEnabled() && (!getBranch()->getVisible() || getBranch()->getTornOff()); + LLMenuGL* branch = getBranch(); + if (!branch) + return; + + BOOL auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff()); // torn off menus don't open sub menus on hover unless they have focus if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus()) { auto_open = FALSE; } // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) - if (getBranch()->getTornOff()) + if (branch->getTornOff()) { auto_open = FALSE; } @@ -1250,14 +1358,14 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight ) } else { - if (getBranch()->getTornOff()) + if (branch->getTornOff()) { - ((LLFloater*)getBranch()->getParent())->setFocus(FALSE); - getBranch()->clearHoverItem(); + ((LLFloater*)branch->getParent())->setFocus(FALSE); + branch->clearHoverItem(); } else { - getBranch()->setVisible( FALSE ); + branch->setVisible( FALSE ); } } } @@ -1284,6 +1392,7 @@ void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility ) { if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff()) { + lldebugs << "Forcing branch to visible. Menu: " << getName() << " Branch: " << getBranch()->getName() << llendl; getBranch()->setVisible(FALSE); } LLMenuItemGL::handleVisibilityChange(new_visibility); @@ -1291,91 +1400,118 @@ void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility ) BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) { - if (getMenu()->getVisible() && getBranch() && getBranch()->getVisible() && key == KEY_LEFT) + LLMenuGL* branch = getBranch(); + if (!branch) + return LLMenuItemGL::handleKeyHere(key, mask); + + // an item is highlighted, my menu is open, and I have an active sub menu or we are in + // keyboard navigation mode + if (getHighlight() + && getMenu()->isOpen() + && (isActive() || LLMenuGL::getKeyboardMode())) { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); - - BOOL handled = getBranch()->clearHoverItem(); - if (getBranch()->getTornOff()) + if (branch->getVisible() && key == KEY_LEFT) { - ((LLFloater*)getBranch()->getParent())->setFocus(FALSE); + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); + + BOOL handled = branch->clearHoverItem(); + if (branch->getTornOff()) + { + ((LLFloater*)branch->getParent())->setFocus(FALSE); + } + if (handled && getMenu()->getTornOff()) + { + ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); + } + return handled; } - if (handled && getMenu()->getTornOff()) - { - ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); - } - return handled; - } - if (getHighlight() && - getMenu()->isOpen() && - key == KEY_RIGHT && getBranch() && !getBranch()->getHighlightedItem()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); - - LLMenuItemGL* itemp = getBranch()->highlightNextItem(NULL); - if (itemp) + if (key == KEY_RIGHT && !branch->getHighlightedItem()) { - return TRUE; + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); + + LLMenuItemGL* itemp = branch->highlightNextItem(NULL); + if (itemp) + { + return TRUE; + } } } - return LLMenuItemGL::handleKeyHere(key, mask); } +//virtual +BOOL LLMenuItemBranchGL::isActive() const +{ + return isOpen() && getBranch() && getBranch()->getHighlightedItem(); +} + +//virtual +BOOL LLMenuItemBranchGL::isOpen() const +{ + return getBranch() && getBranch()->isOpen(); +} + void LLMenuItemBranchGL::openMenu() { - if(!getBranch()) return; + LLMenuGL* branch = getBranch(); + if (!branch) + return; - if (getBranch()->getTornOff()) + if (branch->getTornOff()) { - gFloaterView->bringToFront((LLFloater*)getBranch()->getParent()); + gFloaterView->bringToFront((LLFloater*)branch->getParent()); // this might not be necessary, as torn off branches don't get focus and hence no highligth - getBranch()->highlightNextItem(NULL); + branch->highlightNextItem(NULL); } - else if( !getBranch()->getVisible() ) + else if( !branch->getVisible() ) { // get valid rectangle for menus const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); - getBranch()->arrange(); + branch->arrange(); - LLRect rect = getBranch()->getRect(); + LLRect branch_rect = branch->getRect(); // calculate root-view relative position for branch menu S32 left = getRect().mRight; S32 top = getRect().mTop - getRect().mBottom; - localPointToOtherView(left, top, &left, &top, getBranch()->getParent()); + localPointToOtherView(left, top, &left, &top, branch->getParent()); - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); + branch_rect.setLeftTopAndSize( left, top, + branch_rect.getWidth(), branch_rect.getHeight() ); - if (getBranch()->getCanTearOff()) + if (branch->getCanTearOff()) { - rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); + branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); } - getBranch()->setRect( rect ); - S32 x = 0; - S32 y = 0; - getBranch()->localPointToOtherView( 0, 0, &x, &y, getBranch()->getParent() ); + branch->setRect( branch_rect ); + + // if branch extends outside of menu region change the direction it opens in + S32 x, y; S32 delta_x = 0; S32 delta_y = 0; - if( y < menu_region_rect.mBottom ) + branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); + + F32 center_y = top - (getRect().getHeight() / 2.f); + if( y < menu_region_rect.mBottom && center_y <= menu_region_rect.getCenterY()) { - delta_y = menu_region_rect.mBottom - y; + // open upwards if menu extends past bottom + // adjust by the height of the menu item branch since it is a submenu + delta_y = branch_rect.getHeight() - getRect().getHeight(); } - S32 menu_region_width = menu_region_rect.getWidth(); - if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() ) + if( x + branch_rect.getWidth() > menu_region_rect.mRight ) { // move sub-menu over to left side - delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth()))); + delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth()))); } - getBranch()->translate( delta_x, delta_y ); - getBranch()->setVisible( TRUE ); - getBranch()->getParent()->sendChildToFront(getBranch()); + branch->translate( delta_x, delta_y ); + + branch->setVisible( TRUE ); + branch->getParent()->sendChildToFront(branch); } } @@ -1540,6 +1676,7 @@ BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) LLMenuGL::setKeyboardMode(FALSE); doIt(); make_ui_sound("UISndClick"); + setVisible(TRUE); return TRUE; } @@ -1566,7 +1703,7 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) { BOOL menu_open = getBranch()->getVisible(); // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded - if (getHighlight() && getMenu()->getVisible() && (isActive() || LLMenuGL::getKeyboardMode())) + if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) { if (key == KEY_LEFT) { @@ -1635,7 +1772,7 @@ void LLMenuItemBranchDownGL::draw( void ) if( getHighlight() ) { - gGL.color4fv( getHighlightBGColor().mV ); + gGL.color4fv( mHighlightBackground.mV ); gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } @@ -1648,20 +1785,19 @@ void LLMenuItemBranchDownGL::draw( void ) LLColor4 color; if (getHighlight()) { - color = getHighlightFGColor(); + color = mHighlightForeground; } else if( getEnabled() ) { - color = getEnabledColor(); + color = mEnabledColor; } else { - color = getDisabledColor(); + color = mDisabledColor; } getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, LLFontGL::HCENTER, LLFontGL::BOTTOM, getFontStyle(), font_shadow ); - // underline navigation key only when keyboard navigation has been initiated if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) { @@ -1689,11 +1825,11 @@ void LLMenuItemBranchDownGL::draw( void ) static LLRegisterWidget r1("menu"); // Default constructor -LLMenuGL::LLMenuGL( const std::string& name, const std::string& label, LLHandle parent_floater_handle ) +LLMenuGL::LLMenuGL( const std::string& name, const std::string& label ) : LLUICtrl( name, LLRect(), FALSE, NULL, NULL ), mBackgroundColor( sDefaultBackgroundColor ), mBgVisible( TRUE ), - mParentMenuItem( NULL ), + mHasSelection( FALSE ), mLabel( label ), mDropShadowed( TRUE ), mHorizontalLayout( FALSE ), @@ -1705,20 +1841,21 @@ LLMenuGL::LLMenuGL( const std::string& name, const std::string& label, LLHandle< mTornOff(FALSE), mTearOffItem(NULL), mSpilloverBranch(NULL), + mFirstVisibleItem(NULL), mSpilloverMenu(NULL), - mParentFloaterHandle(parent_floater_handle), - mJumpKey(KEY_NONE) + mJumpKey(KEY_NONE), + mNeedsArrange(FALSE), + mShortcutPad(ACCEL_PAD_PIXELS) { mFadeTimer.stop(); - setCanTearOff(TRUE, parent_floater_handle); setTabStop(FALSE); } -LLMenuGL::LLMenuGL( const std::string& label, LLHandle parent_floater_handle ) +LLMenuGL::LLMenuGL( const std::string& label) : LLUICtrl( label, LLRect(), FALSE, NULL, NULL ), mBackgroundColor( sDefaultBackgroundColor ), mBgVisible( TRUE ), - mParentMenuItem( NULL ), + mHasSelection( FALSE ), mLabel( label ), mDropShadowed( TRUE ), mHorizontalLayout( FALSE ), @@ -1731,11 +1868,12 @@ LLMenuGL::LLMenuGL( const std::string& label, LLHandle parent_floater mTearOffItem(NULL), mSpilloverBranch(NULL), mSpilloverMenu(NULL), - mParentFloaterHandle(parent_floater_handle), - mJumpKey(KEY_NONE) + mFirstVisibleItem(NULL), + mJumpKey(KEY_NONE), + mNeedsArrange(FALSE), + mShortcutPad(ACCEL_PAD_PIXELS) { mFadeTimer.stop(); - setCanTearOff(TRUE, parent_floater_handle); setTabStop(FALSE); } @@ -1748,14 +1886,12 @@ LLMenuGL::~LLMenuGL( void ) mJumpKeys.clear(); } -void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle parent_floater_handle ) +void LLMenuGL::setCanTearOff(BOOL tear_off) { if (tear_off && mTearOffItem == NULL) { - mTearOffItem = new LLMenuItemTearOffGL(parent_floater_handle); - mItems.insert(mItems.begin(), mTearOffItem); - addChildAtEnd(mTearOffItem); - arrange(); + mTearOffItem = new LLMenuItemTearOffGL(); + addChild(mTearOffItem); } else if (!tear_off && mTearOffItem != NULL) { @@ -1763,7 +1899,7 @@ void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle parent_floater_h removeChild(mTearOffItem); delete mTearOffItem; mTearOffItem = NULL; - arrange(); + needsArrange(); } } @@ -1819,12 +1955,17 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory submenu->updateParent(parent); } } - else if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || - child->hasName(LL_MENU_ITEM_CHECK_GL_TAG) || - child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG)) + else if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG)) { - LLMenuItemGL *item = NULL; - + std::string item_name; + child->getAttributeString("name", item_name); + LLMenuItemGL* separator = new LLMenuItemSeparatorGL(item_name); + addChild(separator); + } + else if(child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || + child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) + { + // ITEM std::string type; std::string item_name; std::string source_label; @@ -1834,7 +1975,7 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory child->getAttributeString("type", type); child->getAttributeString("name", item_name); child->getAttributeString("label", source_label); - + // parse jump key out of label typedef boost::tokenizer > tokenizer; boost::char_separator sep("_"); @@ -1851,245 +1992,262 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory ++token_count; } - - if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG)) + MASK mask = 0; + +#ifdef LL_DARWIN + // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd + BOOL useMacCtrl = FALSE; + child->getAttributeBOOL("useMacCtrl", useMacCtrl); +#endif // LL_DARWIN + + std::string shortcut; + child->getAttributeString("shortcut", shortcut); + if (shortcut.find("control") != shortcut.npos) { - appendSeparator(item_name); - } - else - { - // ITEM - if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) || - child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) +#ifdef LL_DARWIN + if ( useMacCtrl ) { - MASK mask = 0; - -#ifdef LL_DARWIN - // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd - BOOL useMacCtrl = FALSE; - child->getAttributeBOOL("useMacCtrl", useMacCtrl); + mask |= MASK_MAC_CONTROL; + } #endif // LL_DARWIN - - std::string shortcut; - child->getAttributeString("shortcut", shortcut); - if (shortcut.find("control") != shortcut.npos) - { -#ifdef LL_DARWIN - if ( useMacCtrl ) - { - mask |= MASK_MAC_CONTROL; - } -#endif // LL_DARWIN - mask |= MASK_CONTROL; - } - if (shortcut.find("alt") != shortcut.npos) - { - mask |= MASK_ALT; - } - if (shortcut.find("shift") != shortcut.npos) - { - mask |= MASK_SHIFT; - } - S32 pipe_pos = shortcut.rfind("|"); - std::string key_str = shortcut.substr(pipe_pos+1); + mask |= MASK_CONTROL; + } + if (shortcut.find("alt") != shortcut.npos) + { + mask |= MASK_ALT; + } + if (shortcut.find("shift") != shortcut.npos) + { + mask |= MASK_SHIFT; + } + S32 pipe_pos = shortcut.rfind("|"); + std::string key_str = shortcut.substr(pipe_pos+1); - KEY key = KEY_NONE; - LLKeyboard::keyFromString(key_str, &key); + KEY key = KEY_NONE; + LLKeyboard::keyFromString(key_str, &key); - LLMenuItemCallGL *new_item; - LLXMLNodePtr call_child; + LLMenuItemCallGL *new_item; + LLXMLNodePtr call_child; - if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) + if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG)) + { + std::string control_name; + child->getAttributeString("control_name", control_name); + + new_item = new LLMenuItemCheckGL(item_name, item_label, 0, 0, control_name, parent, 0, key, mask); + + for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) + { + if (call_child->hasName("on_check")) { + std::string callback_name; std::string control_name; - child->getAttributeString("control_name", control_name); - - new_item = new LLMenuItemCheckGL(item_name, item_label, 0, 0, control_name, parent, 0, key, mask); - - for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) + if (call_child->hasAttribute("function")) { - if (call_child->hasName("on_check")) - { - std::string callback_name; - std::string control_name; - if (call_child->hasAttribute("function")) - { - call_child->getAttributeString("function", callback_name); - - control_name = callback_name; - - std::string callback_data = item_name; - if (call_child->hasAttribute("userdata")) - { - call_child->getAttributeString("userdata", callback_data); - if (!callback_data.empty()) - { - control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); - } - } - - LLSD userdata; - userdata["control"] = control_name; - userdata["data"] = callback_data; - - LLSimpleListener* callback = parent->getListenerByName(callback_name); - - if (!callback) - { - lldebugs << "Ignoring \"on_check\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; - continue; - } - - new_item->addListener(callback, "on_build", userdata); - } - else if (call_child->hasAttribute("control")) - { - call_child->getAttributeString("control", control_name); - } - else - { - continue; - } - LLControlVariable *control = parent->findControl(control_name); - if (!control) - { - parent->addBoolControl(control_name, FALSE); - } - ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name, parent); - } - } - } - else - { - new_item = new LLMenuItemCallGL(item_name, item_label, 0, 0, 0, 0, key, mask); - } - - for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) - { - if (call_child->hasName("on_click")) - { - std::string callback_name; call_child->getAttributeString("function", callback_name); + control_name = callback_name; + std::string callback_data = item_name; if (call_child->hasAttribute("userdata")) { call_child->getAttributeString("userdata", callback_data); + if (!callback_data.empty()) + { + control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); + } } + LLSD userdata; + userdata["control"] = control_name; + userdata["data"] = callback_data; + LLSimpleListener* callback = parent->getListenerByName(callback_name); if (!callback) { - lldebugs << "Ignoring \"on_click\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; + lldebugs << "Ignoring \"on_check\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; continue; } - new_item->addListener(callback, "on_click", callback_data); + new_item->addListener(callback, "on_build", userdata); } - if (call_child->hasName("on_enable")) + else if (call_child->hasAttribute("control")) { - std::string callback_name; - std::string control_name; - if (call_child->hasAttribute("function")) - { - call_child->getAttributeString("function", callback_name); - - control_name = callback_name; - - std::string callback_data; - if (call_child->hasAttribute("userdata")) - { - call_child->getAttributeString("userdata", callback_data); - if (!callback_data.empty()) - { - control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); - } - } - - LLSD userdata; - userdata["control"] = control_name; - userdata["data"] = callback_data; - - LLSimpleListener* callback = parent->getListenerByName(callback_name); - - if (!callback) - { - lldebugs << "Ignoring \"on_enable\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; - continue; - } - - new_item->addListener(callback, "on_build", userdata); - } - else if (call_child->hasAttribute("control")) - { - call_child->getAttributeString("control", control_name); - } - else - { - continue; - } - new_item->setEnabledControl(control_name, parent); + call_child->getAttributeString("control", control_name); } - if (call_child->hasName("on_visible")) + else { - std::string callback_name; - std::string control_name; - if (call_child->hasAttribute("function")) - { - call_child->getAttributeString("function", callback_name); - - control_name = callback_name; - - std::string callback_data; - if (call_child->hasAttribute("userdata")) - { - call_child->getAttributeString("userdata", callback_data); - if (!callback_data.empty()) - { - control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); - } - } - - LLSD userdata; - userdata["control"] = control_name; - userdata["data"] = callback_data; - - LLSimpleListener* callback = parent->getListenerByName(callback_name); - - if (!callback) - { - lldebugs << "Ignoring \"on_visible\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; - continue; - } - - new_item->addListener(callback, "on_build", userdata); - } - else if (call_child->hasAttribute("control")) - { - call_child->getAttributeString("control", control_name); - } - else - { - continue; - } - new_item->setVisibleControl(control_name, parent); + continue; } + LLControlVariable *control = parent->findControl(control_name); + if (!control) + { + parent->addBoolControl(control_name, FALSE); + } + ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name, parent); } - item = new_item; - item->setLabel(item_label); - if (jump_key != KEY_NONE) - item->setJumpKey(jump_key); - } - - if (item != NULL) - { - append(item); } } + else + { + new_item = new LLMenuItemCallGL(item_name, item_label, 0, 0, 0, 0, key, mask); + } + + for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) + { + if (call_child->hasName("on_click")) + { + std::string callback_name; + call_child->getAttributeString("function", callback_name); + + std::string callback_data = item_name; + if (call_child->hasAttribute("userdata")) + { + call_child->getAttributeString("userdata", callback_data); + } + + LLSimpleListener* callback = parent->getListenerByName(callback_name); + + if (!callback) + { + lldebugs << "Ignoring \"on_click\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; + continue; + } + + new_item->addListener(callback, "on_click", callback_data); + } + if (call_child->hasName("on_enable")) + { + std::string callback_name; + std::string control_name; + if (call_child->hasAttribute("function")) + { + call_child->getAttributeString("function", callback_name); + + control_name = callback_name; + + std::string callback_data; + if (call_child->hasAttribute("userdata")) + { + call_child->getAttributeString("userdata", callback_data); + if (!callback_data.empty()) + { + control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); + } + } + + LLSD userdata; + userdata["control"] = control_name; + userdata["data"] = callback_data; + + LLSimpleListener* callback = parent->getListenerByName(callback_name); + + if (!callback) + { + lldebugs << "Ignoring \"on_enable\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; + continue; + } + + new_item->addListener(callback, "on_build", userdata); + } + else if (call_child->hasAttribute("control")) + { + call_child->getAttributeString("control", control_name); + } + else + { + continue; + } + new_item->setEnabledControl(control_name, parent); + } + if (call_child->hasName("on_visible")) + { + std::string callback_name; + std::string control_name; + if (call_child->hasAttribute("function")) + { + call_child->getAttributeString("function", callback_name); + + control_name = callback_name; + + std::string callback_data; + if (call_child->hasAttribute("userdata")) + { + call_child->getAttributeString("userdata", callback_data); + if (!callback_data.empty()) + { + control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str()); + } + } + + LLSD userdata; + userdata["control"] = control_name; + userdata["data"] = callback_data; + + LLSimpleListener* callback = parent->getListenerByName(callback_name); + + if (!callback) + { + lldebugs << "Ignoring \"on_visible\" \"" << item_name << "\" because \"" << callback_name << "\" is not registered" << llendl; + continue; + } + + new_item->addListener(callback, "on_build", userdata); + } + else if (call_child->hasAttribute("control")) + { + call_child->getAttributeString("control", control_name); + } + else + { + continue; + } + new_item->setVisibleControl(control_name, parent); + } + } + new_item->setLabel(item_label); + if (jump_key != KEY_NONE) + new_item->setJumpKey(jump_key); + + append(new_item); } } +bool LLMenuGL::addChild(LLView* view, S32 tab_group) +{ + if (LLMenuGL* menup = dynamic_cast(view)) + { + lldebugs << "Adding menu " << menup->getName() << " to " << getName() << llendl; + appendMenu(menup); + return true; + } + else if (LLMenuItemGL* itemp = dynamic_cast(view)) + { + lldebugs << "Adding " << itemp->getName() << " to " << getName() << llendl; + append(itemp); + return true; + } + lldebugs << "Error adding unknown child '"<<(view ? view->getName() : std::string("NULL")) << "' to " << getName() << llendl; + return false; +} + +void LLMenuGL::removeChild( LLView* ctrl) +{ + // previously a dynamic_cast with if statement to check validity + // unfortunately removeChild is called by ~LLView, and at that point the + // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail + LLMenuItemGL* itemp = static_cast(ctrl); + + item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp)); + if (found_it != mItems.end()) + { + mItems.erase(found_it); + } + + return LLUICtrl::removeChild(ctrl); +} + // are we the childmost active menu and hence our jump keys should be enabled? // or are we a free-standing torn-off menu (which uses jump keys too) BOOL LLMenuGL::jumpKeysActive() @@ -2222,39 +2380,59 @@ void LLMenuGL::arrange( void ) // torn off menus are not constrained to the size of the screen U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth(); - U32 max_height = getTornOff() ? U32_MAX : menu_region_rect.getHeight(); + U32 max_height = U32_MAX; + if (!getTornOff()) + { + max_height = getRect().mTop - menu_region_rect.mBottom; + if (menu_region_rect.mTop - getRect().mTop > (S32)max_height) + { + max_height = menu_region_rect.mTop - getRect().mTop; + } + } + // *FIX: create the item first and then ask for its dimensions? - S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); + S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate S32 spillover_item_height = llround(LLFontGL::getFontSansSerif()->getLineHeight()) + MENU_ITEM_PADDING; + // Scrolling support + item_list_t::iterator first_visible_item_iter; + item_list_t::iterator first_hidden_item_iter = mItems.end(); + //S32 height_before_first_visible_item = -1; + //S32 visible_items_height = 0; + //U32 scrollable_items_cnt = 0; + if (mHorizontalLayout) { item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() - && item_iter != mItems.begin() // Don't spillover the first item! + && *item_iter != mSpilloverBranch && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width) { // no room for any more items createSpilloverBranch(); - item_list_t::iterator spillover_iter; - for (spillover_iter = item_iter; spillover_iter != mItems.end(); ++spillover_iter) + std::vector items_to_remove; + std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove)); + std::vector::iterator spillover_iter; + for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter) { LLMenuItemGL* itemp = (*spillover_iter); removeChild(itemp); - mSpilloverMenu->appendNoArrange(itemp); // *NOTE:Mani Favor addChild() in merge with skinning + mSpilloverMenu->addChild(itemp); } - mSpilloverMenu->arrange(); // *NOTE: Mani Remove line in merge with skinning/viewer2.0 branch - mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); // *NOTE: Mani Remove line in merge with skinning/viewer2.0 branch - mItems.erase(item_iter, mItems.end()); - mItems.push_back(mSpilloverBranch); + addChild(mSpilloverBranch); + height = llmax(height, mSpilloverBranch->getNominalHeight()); width += mSpilloverBranch->getNominalWidth(); + break; } else @@ -2271,29 +2449,33 @@ void LLMenuGL::arrange( void ) item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + llassert_always((*item_iter)!=NULL); + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() - && item_iter != mItems.begin() // Don't spillover the first item! + && *item_iter != mSpilloverBranch && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height) { // no room for any more items createSpilloverBranch(); - item_list_t::iterator spillover_iter; - for (spillover_iter= item_iter; spillover_iter != mItems.end(); ++spillover_iter) + std::vector items_to_remove; + std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove)); + std::vector::iterator spillover_iter; + for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter) { LLMenuItemGL* itemp = (*spillover_iter); removeChild(itemp); - mSpilloverMenu->appendNoArrange(itemp); // *NOTE:Mani Favor addChild() in merge with skinning + mSpilloverMenu->addChild(itemp); } - mSpilloverMenu->arrange(); // *NOTE: Mani Remove line in merge with skinning/viewer2.0 branch - mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); // *NOTE: Mani Remove line in merge with skinning/viewer2.0 branch - mItems.erase(item_iter, mItems.end()); - mItems.push_back(mSpilloverBranch); addChild(mSpilloverBranch); + height += mSpilloverBranch->getNominalHeight(); width = llmax( width, mSpilloverBranch->getNominalWidth() ); + break; } else @@ -2306,10 +2488,11 @@ void LLMenuGL::arrange( void ) } } - setRect(LLRect(getRect().mLeft, getRect().mBottom + height, getRect().mLeft + width, getRect().mBottom)); - S32 cur_height = (S32)llmin(max_height, height); + setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height)); + S32 cur_width = 0; + S32 offset = 0; item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { @@ -2324,15 +2507,15 @@ void LLMenuGL::arrange( void ) } else { - rect.setLeftTopAndSize( 0, cur_height, width, (*item_iter)->getNominalHeight()); - cur_height -= (*item_iter)->getNominalHeight(); + rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight()); + if (offset == 0) + { + cur_height -= (*item_iter)->getNominalHeight(); + } } (*item_iter)->setRect( rect ); - (*item_iter)->buildDrawLabel(); } } - - cleanupSpilloverBranch(); } if (mKeepFixedSize) { @@ -2340,6 +2523,15 @@ void LLMenuGL::arrange( void ) } } +void LLMenuGL::arrangeAndClear( void ) +{ + if (mNeedsArrange) + { + arrange(); + mNeedsArrange = FALSE; + } +} + void LLMenuGL::createSpilloverBranch() { if (!mSpilloverBranch) @@ -2348,7 +2540,7 @@ void LLMenuGL::createSpilloverBranch() delete mSpilloverMenu; // technically, you can't tear off spillover menus, but we're passing the handle // along just to be safe - mSpilloverMenu = new LLMenuGL(std::string("More"), std::string("More"), mParentFloaterHandle); + mSpilloverMenu = new LLMenuGL(std::string("More"), std::string("More")); mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); // Inherit colors mSpilloverMenu->setBackgroundColor( mBackgroundColor ); @@ -2366,22 +2558,12 @@ void LLMenuGL::cleanupSpilloverBranch() // head-recursion to propagate items back up to root menu mSpilloverMenu->cleanupSpilloverBranch(); - removeChild(mSpilloverBranch); - - item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), mSpilloverBranch); - if (found_iter != mItems.end()) - { - mItems.erase(found_iter); - } - // pop off spillover items while (mSpilloverMenu->getItemCount()) { LLMenuItemGL* itemp = mSpilloverMenu->getItem(0); mSpilloverMenu->removeChild(itemp); - mSpilloverMenu->mItems.erase(mSpilloverMenu->mItems.begin()); // put them at the end of our own list - mItems.push_back(itemp); addChild(itemp); } @@ -2495,6 +2677,7 @@ void LLMenuGL::empty( void ) cleanupSpilloverBranch(); mItems.clear(); + mFirstVisibleItem = NULL; deleteAllChildren(); @@ -2504,7 +2687,7 @@ void LLMenuGL::empty( void ) void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) { setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom)); - arrange(); + needsArrange(); } BOOL LLMenuGL::handleJumpKey(KEY key) @@ -2517,9 +2700,6 @@ BOOL LLMenuGL::handleJumpKey(KEY key) // switch to keyboard navigation mode LLMenuGL::setKeyboardMode(TRUE); - // force highlight to close old menus and any open sub-menus - - //clearHoverItem(); // force highlight to close old menus and open and sub-menus found_it->second->setHighlight(TRUE); found_it->second->doIt(); @@ -2534,40 +2714,18 @@ BOOL LLMenuGL::handleJumpKey(KEY key) // Add the menu item to this menu. BOOL LLMenuGL::append( LLMenuItemGL* item ) { - if (mSpilloverMenu) - { - return mSpilloverMenu->append(item); - } - + if (!item) return FALSE; mItems.push_back( item ); - addChild( item ); - arrange(); - return TRUE; -} - -// *NOTE:Mani - appendNoArrange() should be removed when merging to skinning/viewer2.0 -// Its added as a fix to a viewer 1.23 bug that has already been address by skinning work. -BOOL LLMenuGL::appendNoArrange( LLMenuItemGL* item ) -{ - if (mSpilloverMenu) - { - return mSpilloverMenu->append(item); - } - - mItems.push_back( item ); - addChild( item ); + LLUICtrl::addChild(item); + needsArrange(); return TRUE; } // add a separator to this menu -BOOL LLMenuGL::appendSeparator( const std::string &separator_name ) +BOOL LLMenuGL::addSeparator() { - LLMenuItemGL* separator; - if (separator_name.empty()) - separator = new LLMenuItemSeparatorGL(std::string("separator")); - else - separator = new LLMenuItemSeparatorGL(separator_name); - return append( separator ); + LLMenuItemGL* separator = new LLMenuItemSeparatorGL(); + return addChild(separator); } // add a menu - this will create a cascading menu @@ -2587,35 +2745,10 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu ) // Inherit colors menu->setBackgroundColor( mBackgroundColor ); - + menu->updateParent(LLMenuGL::sMenuContainer); return success; } -// Remove a menu item from this menu. -BOOL LLMenuGL::remove( LLMenuItemGL* item ) -{ - if (mSpilloverMenu) - { - cleanupSpilloverBranch(); - } - - item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), item); - if (found_iter != mItems.end()) - { - mItems.erase(found_iter); - } - - removeChild( item ); - - // We keep it around in case someone is pointing at it. - // The caller can delete it if it's safe. - // Note that getMenu() will still not work since its parent isn't a menu. - sMenuContainer->addChild( item ); - - arrange(); - return TRUE; -} - void LLMenuGL::setEnabledSubMenus(BOOL enable) { setEnabled(enable); @@ -2650,6 +2783,7 @@ void LLMenuGL::setItemVisible( const std::string& name, BOOL visible ) if( (*item_iter)->getName() == name ) { (*item_iter)->setVisible( visible ); + needsArrange(); break; } } @@ -2714,6 +2848,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem() LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -2721,14 +2856,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa ((LLFloater*)getParent())->setFocus(TRUE); } - item_list_t::iterator cur_item_iter; - for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter) - { - if( (*cur_item_iter) == cur_item) - { - break; - } - } + // Current item position in the items list + item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item); item_list_t::iterator next_item_iter; if (cur_item_iter == mItems.end()) @@ -2739,6 +2868,10 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa { next_item_iter = cur_item_iter; next_item_iter++; + + // First visible item position in the items list + item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem); + if (next_item_iter == mItems.end()) { next_item_iter = mItems.begin(); @@ -2760,7 +2893,7 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa while(1) { // skip separators and disabled/invisible items - if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && (*next_item_iter)->getType() != SEPARATOR_NAME) + if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast(*next_item_iter)) { if (cur_item) { @@ -2792,6 +2925,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; + // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -2799,14 +2934,8 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa ((LLFloater*)getParent())->setFocus(TRUE); } - item_list_t::reverse_iterator cur_item_iter; - for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter) - { - if( (*cur_item_iter) == cur_item) - { - break; - } - } + // Current item reverse position from the end of the list + item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item); item_list_t::reverse_iterator prev_item_iter; if (cur_item_iter == mItems.rend()) @@ -2817,6 +2946,10 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa { prev_item_iter = cur_item_iter; prev_item_iter++; + + // First visible item reverse position in the items list + item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem); + if (prev_item_iter == mItems.rend()) { prev_item_iter = mItems.rbegin(); @@ -2826,7 +2959,7 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa while(1) { // skip separators and disabled/invisible items - if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getType() != SEPARATOR_NAME) + if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) { (*prev_item_iter)->setHighlight(TRUE); return (*prev_item_iter); @@ -2867,17 +3000,15 @@ void LLMenuGL::updateParent(LLView* parentp) { getParent()->removeChild(this); } - parentp->addChild(this); + if (parentp) + { + parentp->addChild(this); + } item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { (*item_iter)->updateBranchParent(parentp); } - - if (mSpilloverMenu) - { - mSpilloverMenu->updateParent(parentp); - } } BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) @@ -2929,11 +3060,11 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) // don't change menu focus unless mouse is moving or alt key is not held down if ((llabs(mMouseVelX) > 0 || - llabs(mMouseVelY) > 0) && + llabs(mMouseVelY) > 0) /*&& (!mHasSelection || - //(mouse_delta_x == 0 && mouse_delta_y == 0) || + (mouse_delta_x == 0 && mouse_delta_y == 0) || (mMouseVelX < 0) || - llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU)) + llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU)*/) { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { @@ -2970,31 +3101,59 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) ((LLMenuItemGL*)viewp)->setHighlight(TRUE); LLMenuGL::setKeyboardMode(FALSE); } - mHasSelection = TRUE; + mHasSelection = true; } } } getWindow()->setCursor(UI_CURSOR_ARROW); + + // *HACK Release the mouse capture + // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button + // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events + // until the user chooses one of the drop-down menu items. + return TRUE; } +BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) +{ + return blockMouseEvent(x, y); +} + void LLMenuGL::draw( void ) { + if (mNeedsArrange) + { + arrange(); + mNeedsArrange = FALSE; + } if (mDropShadowed && !mTornOff) { - static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow"); static S32 drop_shadow_floater = LLUI::sConfigGroup->getS32("DropShadowFloater"); + static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow"); + gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow, - drop_shadow_floater ); + color_drop_shadow, drop_shadow_floater ); } - LLColor4 bg_color = mBackgroundColor; - if( mBgVisible ) { gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor ); } + + + /*LLRect rootRect = getRootView()->getRect(); + for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + { + LLMenuItemGL* itemp = static_cast(*child_it); + if(itemp) + { + LLRect screenRect; + localRectToScreen(itemp->getRect(),&screenRect); + lldebugs << itemp->getName() << "Visible:" << itemp->getVisible() << " ValidRect: " << itemp->getRect().isValid() << " Overlaps: " << rootRect.overlaps(screenRect) << llendl; + } + }*/ + LLView::draw(); } @@ -3009,8 +3168,10 @@ void LLMenuGL::setVisible(BOOL visible) { if (visible != getVisible()) { + LLView::setVisible(visible); if (!visible) { + lldebugs << "Hiding " << getName() << llendl; mFadeTimer.start(); clearHoverItem(); // reset last known mouse coordinates so @@ -3019,12 +3180,11 @@ void LLMenuGL::setVisible(BOOL visible) mLastMouseY = 0; } else - { - mHasSelection = FALSE; + { + lldebugs << "Showing " << getName() << llendl; + mHasSelection = true; mFadeTimer.stop(); } - - LLView::setVisible(visible); } } @@ -3069,48 +3229,60 @@ void hide_top_view( LLView* view ) } +// x and y are the desired location for the popup, in the spawning_view's +// coordinate frame, NOT necessarily the mouse location // static void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) { - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); + const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 12; + + if(menu->getChildList()->empty()) + { + return; + } + + // Save click point for detecting cursor moves before mouse-up. + // Must be in local coords to compare with mouseUp events. + // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLContextMenu::show() + S32 mouse_x, mouse_y; + + // Resetting scrolling position + if (menu->isScrollable() && menu->isScrollPositionOnShowReset()) + { + menu->mFirstVisibleItem = NULL; + } + + menu->setVisible( TRUE ); + + // Fix menu rect if needed. + menu->needsArrange(); + menu->arrangeAndClear(); + + LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); + LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); + + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect(); const S32 HPAD = 2; LLRect rect = menu->getRect(); - //LLView* cur_view = spawning_view; S32 left = x + HPAD; S32 top = y; spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); rect.setLeftTopAndSize( left, top, rect.getWidth(), rect.getHeight() ); - - - //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight()); menu->setRect( rect ); - S32 bottom; - left = rect.mLeft; - bottom = rect.mBottom; - //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom, - // &left, &bottom ); - S32 delta_x = 0; - S32 delta_y = 0; - if( bottom < menu_region_rect.mBottom ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - //delta_y = menu_region_rect.mBottom - bottom; - delta_y = (rect.getHeight() + 2 * HPAD); - } - if( left > menu_region_rect.mRight - rect.getWidth() ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - //delta_x = (window_width - rect.getWidth()) - x; - delta_x = -(rect.getWidth() + 2 * HPAD); - } - menu->translate( delta_x, delta_y ); - menu->setVisible( TRUE ); + // Adjust context menu to fit onscreen + LLRect mouse_rect; + const S32 MOUSE_CURSOR_PADDING = 5; + mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, + mouse_y + MOUSE_CURSOR_PADDING, + CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, + CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); + menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE ); menu->getParent()->sendChildToFront(menu); } @@ -3128,6 +3300,12 @@ public: // called to rebuild the draw label virtual void buildDrawLabel( void ); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) + { + LLMenuItemGL::handleMouseUp(x,y,mask); + return TRUE; + } + // doIt() - do the primary funcationality of the menu item. virtual void doIt( void ); @@ -3137,6 +3315,9 @@ protected: LLPieMenu* mBranch; }; +const F32 PIE_MENU_WIDTH = 190; +const F32 PIE_MENU_HEIGHT = 190; + LLPieMenuBranch::LLPieMenuBranch(const std::string& name, const std::string& label, LLPieMenu* branch) @@ -3219,8 +3400,8 @@ LLPieMenu::LLPieMenu(const std::string& name, const std::string& label) mCurRadius(0.f), mRightMouseDown(FALSE) { + setRect(LLRect(0,PIE_MENU_HEIGHT,PIE_MENU_WIDTH,0)); LLMenuGL::setVisible(FALSE); - setCanTearOff(FALSE); } LLPieMenu::LLPieMenu(const std::string& name) @@ -3234,8 +3415,8 @@ LLPieMenu::LLPieMenu(const std::string& name) mCurRadius(0.f), mRightMouseDown(FALSE) { + setRect(LLRect(0,PIE_MENU_HEIGHT,PIE_MENU_WIDTH,0)); LLMenuGL::setVisible(FALSE); - setCanTearOff(FALSE); } @@ -3273,6 +3454,18 @@ void LLPieMenu::initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *fac } } +bool LLPieMenu::addChild(LLView* view, S32 tab_group) +{ + if(LLMenuGL::addChild(view, tab_group)) + { + LLMenuItemSeparatorGL* sep = dynamic_cast(view); + if(sep) + sep->setVisible(false); + return true; + } + return false; +} + // virtual void LLPieMenu::setVisible(BOOL visible) { @@ -3396,7 +3589,7 @@ BOOL LLPieMenu::handleMouseDown( S32 x, S32 y, MASK mask ) } // always handle mouse down as mouse up will close open menus - return handled; + return TRUE; } BOOL LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) @@ -3637,7 +3830,7 @@ BOOL LLPieMenu::append(LLMenuItemGL *item) } // virtual -BOOL LLPieMenu::appendSeparator(const std::string &separator_name) +BOOL LLPieMenu::addSeparator() { LLMenuItemGL* separator = new LLMenuItemBlankGL(); separator->setFont( LLFontGL::getFontSansSerifSmall() ); @@ -3661,9 +3854,6 @@ BOOL LLPieMenu::appendPieMenu(LLPieMenu *menu) // virtual void LLPieMenu::arrange() { - const S32 rect_height = 190; - const S32 rect_width = 190; - // all divide by 6 const S32 CARD_X = 60; const S32 DIAG_X = 48; @@ -3673,8 +3863,6 @@ void LLPieMenu::arrange() const S32 ITEM_CENTER_X[] = { CARD_X, DIAG_X, 0, -DIAG_X, -CARD_X, -DIAG_X, 0, DIAG_X }; const S32 ITEM_CENTER_Y[] = { 0, DIAG_Y, CARD_Y, DIAG_Y, 0, -DIAG_Y, -CARD_Y, -DIAG_Y }; - LLRect rect; - S32 font_height = 0; if( mItems.size() ) { @@ -3687,8 +3875,7 @@ void LLPieMenu::arrange() // TODO: Compute actual bounding rect for menu - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).setOriginAndSize(getRect().mLeft, getRect().mBottom, rect_width, rect_height ); + LLRect rect = getRect(); // place items around a circle, with item 0 at positive X, // rotating counter-clockwise @@ -3706,7 +3893,7 @@ void LLPieMenu::arrange() item_width, font_height ); // Correct for the actual rectangle size - rect.translate( rect_width/2, rect_height/2 ); + rect.translate( getRect().getWidth()/2, getRect().getHeight()/2 ); item->setRect( rect ); @@ -3760,7 +3947,10 @@ LLMenuItemGL *LLPieMenu::pieItemFromXY(S32 x, S32 y) { if (which == 0) { - return (*item_iter); + if((*item_iter)->getVisible()) + return (*item_iter); + else + return NULL; } which--; } @@ -3804,62 +3994,38 @@ void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down) const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); LLView* parent_view = getParent(); - BOOL moved = FALSE; S32 local_x, local_y; parent_view->screenPointToLocal(x, y, &local_x, &local_y); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).setCenterAndSize(local_x, local_y, width, height); + LLRect rect; + rect.setCenterAndSize(local_x, local_y, width, height); + setRect(rect); + arrange(); // Adjust the pie rectangle to keep it on screen - if (getRect().mLeft < menu_region_rect.mLeft) + if(!menu_region_rect.contains(rect)) { - //mShiftHoriz = menu_region_rect.mLeft - getRect().mLeft; - //getRect().translate( mShiftHoriz, 0 ); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).translate( menu_region_rect.mLeft - getRect().mLeft, 0 ); - moved = TRUE; - } - - if (getRect().mRight > menu_region_rect.mRight) - { - //mShiftHoriz = menu_region_rect.mRight - getRect().mRight; - //getRect().translate( mShiftHoriz, 0); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).translate( menu_region_rect.mRight - getRect().mRight, 0 ); - moved = TRUE; - } - - if (getRect().mBottom < menu_region_rect.mBottom) - { - //mShiftVert = menu_region_rect.mBottom - getRect().mBottom; - //getRect().translate( 0, mShiftVert ); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).translate( 0, menu_region_rect.mBottom - getRect().mBottom ); - moved = TRUE; - } - - - if (getRect().mTop > menu_region_rect.mTop) - { - //mShiftVert = menu_region_rect.mTop - getRect().mTop; - //getRect().translate( 0, mShiftVert ); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast(getRect()).translate( 0, menu_region_rect.mTop - getRect().mTop ); - moved = TRUE; - } - - // If we had to relocate the pie menu, put the cursor in the - // center of its rectangle - if (moved) - { - LLCoordGL center; - center.mX = (getRect().mLeft + getRect().mRight) / 2; - center.mY = (getRect().mTop + getRect().mBottom) / 2; - - LLUI::setMousePositionLocal(getParent(), center.mX, center.mY); + S32 trans[2]={0,0}; + if (rect.mLeft < menu_region_rect.mLeft) + { + trans[0] = menu_region_rect.mLeft - rect.mLeft; + } + else if (rect.mRight > menu_region_rect.mRight) + { + trans[0] = menu_region_rect.mRight - rect.mRight; + } + if (rect.mBottom < menu_region_rect.mBottom) + { + trans[1] = menu_region_rect.mBottom - rect.mBottom; + } + else if (rect.mTop > menu_region_rect.mTop) + { + trans[1] = menu_region_rect.mTop - rect.mTop; + } + setRect(rect.translate(trans[0],trans[1])); + LLUI::setMousePositionLocal(getParent(),rect.getCenterX(), rect.getCenterY()); } // *FIX: what happens when mouse buttons reversed? @@ -3918,10 +4084,10 @@ void LLPieMenu::hide(BOOL item_selected) static LLRegisterWidget r2("menu_bar"); // Default constructor -LLMenuBarGL::LLMenuBarGL( const std::string& name ) : LLMenuGL ( name, name ) +LLMenuBarGL::LLMenuBarGL( const std::string& name ) +: LLMenuGL ( name, name ) { mHorizontalLayout = TRUE; - setCanTearOff(FALSE); mKeepFixedSize = TRUE; mAltKeyTrigger = FALSE; } @@ -4006,13 +4172,6 @@ LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory if (child->hasName("menu")) { LLMenuGL *menu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory); - // because of lazy initialization, have to disable tear off functionality - // and then re-enable with proper parent handle - if (menu->getCanTearOff()) - { - menu->setCanTearOff(FALSE); - menu->setCanTearOff(TRUE, parent_handle); - } menubar->appendMenu(menu); if (LLMenuGL::sMenuContainer != NULL) { @@ -4052,7 +4211,10 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) mAltKeyTrigger = FALSE; } - if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) + if(!result + && (key == KEY_F10 && mask == MASK_CONTROL) + && !gKeyboard->getKeyRepeated(key) + && isInVisibleChain()) { if (getHighlightedItem()) { @@ -4123,19 +4285,22 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) return LLMenuGL::handleMouseDown(x, y, mask); } -BOOL LLMenuBarGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +void LLMenuBarGL::setVisible(BOOL visible) { - // clicks on menu bar closes existing menus from other contexts but leave - // own menu open so that we get toggle behavior - if (!getHighlightedItem() || !getHighlightedItem()->isActive()) + if(visible != getVisible()) { - LLMenuGL::sMenuContainer->hideMenus(); + if(!visible) + { + lldebugs << "Hiding " << getName() << llendl; + } + else + { + lldebugs << "Showing " << getName() << llendl; + } } - - return LLMenuGL::handleMouseDown(x, y, mask); + LLUICtrl::setVisible(visible); } - void LLMenuBarGL::draw() { LLMenuItemGL* itemp = getHighlightedItem(); @@ -4229,7 +4394,7 @@ S32 LLMenuBarGL::getRightmostMenuEdge() } // add a vertical separator to this menu -BOOL LLMenuBarGL::appendSeparator( const std::string &separator_name ) +BOOL LLMenuBarGL::addSeparator() { LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); return append( separator ); @@ -4251,6 +4416,8 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) success &= branch->addToAcceleratorList(&mAccelerators); success &= append( branch ); branch->setJumpKey(branch->getJumpKey()); + menu->updateParent(LLMenuGL::sMenuContainer); + return success; } @@ -4327,6 +4494,7 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) ///============================================================================ /// Class LLMenuHolderGL ///============================================================================ +LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX); LLMenuHolderGL::LLMenuHolderGL() : LLPanel(std::string("Menu Holder")) { @@ -4349,7 +4517,7 @@ void LLMenuHolderGL::draw() LLView::draw(); // now draw last selected item as overlay LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); - if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) + if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) { // make sure toggle items, for example, show the proper state when fading out selecteditem->buildDrawLabel(); @@ -4358,10 +4526,10 @@ void LLMenuHolderGL::draw() selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this); F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME; - F32 alpha = lerp(LLMenuItemGL::getHighlightBGColor().mV[VALPHA], 0.f, interpolant); - LLColor4 bg_color(LLMenuItemGL::getHighlightBGColor().mV[VRED], - LLMenuItemGL::getHighlightBGColor().mV[VGREEN], - LLMenuItemGL::getHighlightBGColor().mV[VBLUE], + F32 alpha = lerp(LLMenuItemGL::sHighlightBackground.mV[VALPHA], 0.f, interpolant); + LLColor4 bg_color(LLMenuItemGL::sHighlightBackground.mV[VRED], + LLMenuItemGL::sHighlightBackground.mV[VGREEN], + LLMenuItemGL::sHighlightBackground.mV[VBLUE], alpha); LLUI::pushMatrix(); @@ -4396,6 +4564,68 @@ BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) return handled; } +// This occurs when you mouse-down to spawn a context menu, hold the button +// down, move off the menu, then mouse-up. We want this to close the menu. +BOOL LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) +{ + const S32 SLOP = 2; + S32 spawn_dx = (x - sContextMenuSpawnPos.mX); + S32 spawn_dy = (y - sContextMenuSpawnPos.mY); + if (-SLOP <= spawn_dx && spawn_dx <= SLOP + && -SLOP <= spawn_dy && spawn_dy <= SLOP) + { + // we're still inside the slop region from spawning this menu + // so interpret the mouse-up as a single-click to show and leave on + // screen + sContextMenuSpawnPos.set(S32_MAX, S32_MAX); + return TRUE; + } + + BOOL handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL; + if (!handled) + { + // clicked off of menu, hide them all + hideMenus(); + } + return handled; +} + +BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + BOOL handled = false; + LLMenuGL* const pMenu = dynamic_cast(getVisibleMenu()); + + if (pMenu) + { + //eat TAB key - EXT-7000 + if (key == KEY_TAB && mask == MASK_NONE) + { + return TRUE; + } + + //handle ESCAPE and RETURN key + handled = LLPanel::handleKey(key, mask, called_from_parent); + if (!handled) + { + if (pMenu->getHighlightedItem()) + { + handled = pMenu->handleKey(key, mask, TRUE); + } + else + { + //highlight first enabled one + if(pMenu->highlightNextItem(NULL)) + { + handled = true; + } + } + } + } + + return handled; + +} + void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) { if (width != getRect().getWidth() || height != getRect().getHeight()) @@ -4405,17 +4635,17 @@ void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) LLView::reshape(width, height, called_from_parent); } -BOOL LLMenuHolderGL::hasVisibleMenu() const +LLView* const LLMenuHolderGL::getVisibleMenu() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { LLView* viewp = *child_it; - if (viewp->getVisible() && dynamic_cast(viewp) == NULL) + if (viewp->getVisible() && dynamic_cast(viewp) != NULL && !dynamic_cast(viewp)) { - return TRUE; + return viewp; } } - return FALSE; + return NULL; } @@ -4436,8 +4666,7 @@ BOOL LLMenuHolderGL::hideMenus() for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { LLView* viewp = *child_it; - // clicks off of menu do not hide menu bar - if (dynamic_cast(viewp) == NULL && viewp->getVisible()) + if (dynamic_cast(viewp) != NULL && viewp->getVisible() && !dynamic_cast(viewp)) { viewp->setVisible(FALSE); } @@ -4463,24 +4692,33 @@ void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : LLFloater(menup->getName(), LLRect(0, 100, 100, 0), menup->getLabel(), FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, FALSE) { + S32 floater_header_size = LLFLOATER_HEADER_SIZE; + + setName(menup->getName()); + setTitle(menup->getLabel()); + setCanMinimize(FALSE); // flag menu as being torn off menup->setTornOff(TRUE); // update menu layout as torn off menu (no spillover menus) - menup->arrange(); + menup->needsArrange(); LLRect rect; menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView); // make sure this floater is big enough for menu - mTargetHeight = (F32)(rect.getHeight() + LLFLOATER_HEADER_SIZE + 5); + mTargetHeight = (F32)(rect.getHeight() + floater_header_size); reshape(rect.getWidth(), rect.getHeight()); setRect(rect); // attach menu to floater - menup->setFollowsAll(); + menup->setFollows(FOLLOWS_BOTTOM|FOLLOWS_LEFT); mOldParent = menup->getParent(); addChild(menup); menup->setVisible(TRUE); - menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1); + + LLRect menu_rect = menup->getRect(); + menu_rect.setOriginAndSize( 1, 1, + menu_rect.getWidth(), menu_rect.getHeight()); + menup->setRect(menu_rect); menup->setDropShadowed(FALSE); mMenu = menup; @@ -4493,19 +4731,13 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : void LLTearOffMenu::draw() { mMenu->setBackgroundVisible(isBackgroundOpaque()); - mMenu->arrange(); + mMenu->needsArrange(); if (getRect().getHeight() != mTargetHeight) { // animate towards target height reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f)))); } - else - { - // when in stasis, remain big enough to hold menu contents - mTargetHeight = (F32)(mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 4); - reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 5); - } LLFloater::draw(); } diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index c4325ac4e..581fcc1a5 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -76,32 +76,40 @@ typedef void (*label_callback)(std::string&,void*); // The LLMenuItemGL represents a single menu item in a menu. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLMenuItemGL : public LLView +class LLMenuItemGL : public LLUICtrl { public: // static functions to control the global color scheme. - static void setEnabledColor( const LLColor4& color ) { sEnabledColor = color; } + /*static void setEnabledColor( const LLColor4& color ) { sEnabledColor = color; } static const LLColor4& getEnabledColor() { return sEnabledColor; } static void setDisabledColor( const LLColor4& color ) { sDisabledColor = color; } static const LLColor4& getDisabledColor() { return sDisabledColor; } static void setHighlightBGColor( const LLColor4& color ) { sHighlightBackground = color; } static const LLColor4& getHighlightBGColor() { return sHighlightBackground; } static void setHighlightFGColor( const LLColor4& color ) { sHighlightForeground = color; } - static const LLColor4& getHighlightFGColor() { return sHighlightForeground; } + static const LLColor4& getHighlightFGColor() { return sHighlightForeground; }*/ LLMenuItemGL( const std::string& name, const std::string& label, KEY key = KEY_NONE, MASK = MASK_NONE ); - virtual ~LLMenuItemGL() {}; + virtual ~LLMenuItemGL(); - virtual void setValue(const LLSD& value) { setLabel(value.asString()); } virtual LLXMLNodePtr getXML(bool save_children = true) const; virtual std::string getType() const { return "item"; } - virtual BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ void handleVisibilityChange(BOOL new_visibility); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + + // LLUICtrl overrides + /*virtual*/ void setValue(const LLSD& value); + /*virtual*/ LLSD getValue() const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); + LLColor4 getHighlightBgColor() { return mHighlightBackground; } + void setJumpKey(KEY key); KEY getJumpKey() const { return mJumpKey; } @@ -115,8 +123,8 @@ public: virtual U32 getNominalHeight( void ) const; // Marks item as not needing space for check marks or accelerator keys - virtual void setBriefItem(BOOL brief) { mBriefItem = brief; } - virtual BOOL isBriefItem() const { return mBriefItem; } + virtual void setBriefItem(BOOL brief); + virtual BOOL isBriefItem() const; virtual BOOL addToAcceleratorList(std::list *listp); void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } @@ -128,7 +136,7 @@ public: virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); // Get the parent menu for this item - virtual class LLMenuGL* getMenu(); + virtual class LLMenuGL* getMenu() const; // returns the normal width of this control in pixels - this is // used for calculating the widest item, as well as for horizontal @@ -167,6 +175,7 @@ public: virtual BOOL handleKeyHere( KEY key, MASK mask ); virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); + virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); virtual void draw( void ); BOOL getHover() const { return mGotHover; } @@ -193,13 +202,19 @@ protected: LLUIString mDrawAccelLabel; LLUIString mDrawBranchLabel; + LLColor4 mEnabledColor; + LLColor4 mDisabledColor; + LLColor4 mHighlightBackground; + LLColor4 mHighlightForeground; + BOOL mHighlight; -private: +public: static LLColor4 sEnabledColor; static LLColor4 sDisabledColor; static LLColor4 sHighlightBackground; static LLColor4 sHighlightForeground; +private: // Keyboard and mouse variables BOOL mAllowKeyRepeat; BOOL mGotHover; @@ -216,6 +231,30 @@ private: KEY mJumpKey; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLMenuItemSeparatorGL +// +// This class represents a separator. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLMenuItemSeparatorGL : public LLMenuItemGL +{ +public: + LLMenuItemSeparatorGL( const std::string &name = std::string() ); + + virtual LLXMLNodePtr getXML(bool save_children = true) const; + + virtual std::string getType() const { return "separator"; } + + // doIt() - do the primary funcationality of the menu item. + virtual void doIt( void ) {} + + virtual void draw( void ); + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual BOOL handleHover(S32 x, S32 y, MASK mask); + + virtual U32 getNominalHeight( void ) const; +}; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemCallGL @@ -278,6 +317,9 @@ public: void setUserData(void *userdata) { mUserData = userdata; } void* getUserData() const { return mUserData; } +protected: + void updateEnabled( void ); +public: // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -285,7 +327,8 @@ public: virtual void doIt( void ); virtual BOOL handleAcceleratorKey(KEY key, MASK mask); - + virtual BOOL handleKeyHere(KEY key, MASK mask); + //virtual void draw(); @@ -403,24 +446,35 @@ class LLMenuGL // TODO: The menu and menu item classes share a great deal of functionality and perhaps should be united. // I think it may make the most sense to make LLMenuGL be a subclass of LLMenuItemGL. -MG { +public: + // textual artwork which menugl-imitators may want to match + static const std::string BOOLEAN_TRUE_PREFIX; + static const std::string BRANCH_SUFFIX; + static const std::string ARROW_UP; + static const std::string ARROW_DOWN; + +protected: // let branching menu items use my protected traversal methods friend class LLMenuItemBranchGL; public: - LLMenuGL( const std::string& name, const std::string& label, LLHandle parent_floater = LLHandle()); - LLMenuGL( const std::string& label, LLHandle parent_floater = LLHandle() ); + LLMenuGL( const std::string& name, const std::string& label); + LLMenuGL( const std::string& label); virtual ~LLMenuGL( void ); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory); - // LLView Functionality - virtual BOOL handleUnicodeCharHere( llwchar uni_char ); - virtual BOOL handleHover( S32 x, S32 y, MASK mask ); - virtual void draw( void ); + /*virtual*/ BOOL handleUnicodeCharHere( llwchar uni_char ); + /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); + /*virtual*/ BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + /*virtual*/ void draw( void ); virtual void drawBackground(LLMenuItemGL* itemp, LLColor4& color); - virtual void setVisible(BOOL visible); + /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + /*virtual*/ void removeChild( LLView* ctrl); + /*virtual*/ BOOL postBuild(); virtual BOOL handleAcceleratorKey(KEY key, MASK mask); @@ -437,23 +491,10 @@ public: void setBackgroundColor( const LLColor4& color ) { mBackgroundColor = color; } const LLColor4& getBackgroundColor() const { return mBackgroundColor; } void setBackgroundVisible( BOOL b ) { mBgVisible = b; } - void setCanTearOff(BOOL tear_off, LLHandle parent_floater_handle = LLHandle()); - - // Add the menu item to this menu. - virtual BOOL append( LLMenuItemGL* item ); - - // Remove a menu item from this menu. - virtual BOOL remove( LLMenuItemGL* item ); - - // *NOTE:Mani - appendNoArrange() should be removed when merging to skinning/viewer2.0 - // Its added as a fix to a viewer 1.23 bug that has already been address by skinning work. - virtual BOOL appendNoArrange( LLMenuItemGL* item ); + void setCanTearOff(BOOL tear_off); // add a separator to this menu - virtual BOOL appendSeparator( const std::string &separator_name = LLStringUtil::null ); - - // add a menu - this will create a cascading menu - virtual BOOL appendMenu( LLMenuGL* menu ); + virtual BOOL addSeparator(); // for branching menu items, bring sub menus up to root level of menu hierarchy virtual void updateParent( LLView* parentp ); @@ -477,19 +518,17 @@ public: virtual BOOL isOpen(); + void needsArrange() { mNeedsArrange = TRUE; } // Shape this menu to fit the current state of the children, and // adjust the child rects to fit. This is called automatically // when you add items. *FIX: We may need to deal with visibility // arrangement. virtual void arrange( void ); + void arrangeAndClear( void ); // remove all items on the menu void empty( void ); - // Rearrange the components, and do the right thing if the menu doesn't - // fit in the bounds. - // virtual void arrangeWithBounds(LLRect bounds); - void setItemLastSelected(LLMenuItemGL* item); // must be in menu U32 getItemCount(); // number of menu items LLMenuItemGL* getItem(S32 number); // 0 = first item @@ -510,8 +549,8 @@ public: // Whether to drop shadow menu bar void setDropShadowed( const BOOL shadowed ); - void setParentMenuItem( LLMenuItemGL* parent_menu_item ) { mParentMenuItem = parent_menu_item; } - LLMenuItemGL* getParentMenuItem() const { return mParentMenuItem; } + void setParentMenuItem( LLMenuItemGL* parent_menu_item ) { mParentMenuItem = parent_menu_item->getHandle(); } + LLMenuItemGL* getParentMenuItem() const { return dynamic_cast(mParentMenuItem.get()); } void setTornOff(BOOL torn_off); BOOL getTornOff() { return mTornOff; } @@ -524,15 +563,26 @@ public: static void setKeyboardMode(BOOL mode) { sKeyboardMode = mode; } static BOOL getKeyboardMode() { return sKeyboardMode; } + S32 getShortcutPad() { return mShortcutPad; } + BOOL isScrollable() const { return FALSE; } + static class LLMenuHolderGL* sMenuContainer; + bool isScrollPositionOnShowReset() { return false; } protected: void createSpilloverBranch(); void cleanupSpilloverBranch(); + // Add the menu item to this menu. + virtual BOOL append( LLMenuItemGL* item ); + + // add a menu - this will create a cascading menu + virtual BOOL appendMenu( LLMenuGL* menu ); // TODO: create accessor methods for these? typedef std::list< LLMenuItemGL* > item_list_t; item_list_t mItems; + LLMenuItemGL*mFirstVisibleItem; + typedef std::map navigation_key_map_t; navigation_key_map_t mJumpKeys; S32 mLastMouseX; @@ -541,6 +591,7 @@ protected: S32 mMouseVelY; BOOL mHorizontalLayout; BOOL mKeepFixedSize; + BOOL mNeedsArrange; private: static LLColor4 sDefaultBackgroundColor; @@ -548,17 +599,17 @@ private: LLColor4 mBackgroundColor; BOOL mBgVisible; - LLMenuItemGL* mParentMenuItem; + LLHandle mParentMenuItem; LLUIString mLabel; BOOL mDropShadowed; // Whether to drop shadow - BOOL mHasSelection; + bool mHasSelection; LLFrameTimer mFadeTimer; BOOL mTornOff; class LLMenuItemTearOffGL* mTearOffItem; class LLMenuItemBranchGL* mSpilloverBranch; LLMenuGL* mSpilloverMenu; - LLHandle mParentFloaterHandle; KEY mJumpKey; + S32 mShortcutPad; }; // end class LLMenuGL @@ -604,11 +655,11 @@ public: virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL isActive() const { return isOpen() && getBranch()->getHighlightedItem(); } + virtual BOOL isActive() const; - virtual BOOL isOpen() const { return getBranch() && getBranch()->isOpen(); } + virtual BOOL isOpen() const; - LLMenuGL *getBranch() const { return (LLMenuGL*)(mBranch.get()); } + LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); } virtual void updateBranchParent( LLView* parentp ); @@ -624,7 +675,7 @@ public: virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; private: - LLHandle mBranch; + LLHandle mBranchHandle; }; // end class LLMenuItemBranchGL @@ -646,6 +697,9 @@ public: void initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *factory); // LLView Functionality + // hide separators. they are added to 'pad' in empty cells. + virtual bool addChild(LLView* view, S32 tab_group = 0); + // can't set visibility directly, must call show or hide virtual void setVisible(BOOL visible); @@ -657,8 +711,10 @@ public: virtual void draw(); virtual void drawBackground(LLMenuItemGL* itemp, LLColor4& color); +private: virtual BOOL append(LLMenuItemGL* item); - virtual BOOL appendSeparator( const std::string &separator_name = LLStringUtil::null ); +public: + virtual BOOL addSeparator(); BOOL appendPieMenu(LLPieMenu *menu); @@ -703,23 +759,17 @@ public: virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); - virtual BOOL handleAcceleratorKey(KEY key, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleJumpKey(KEY key); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + void setVisible(BOOL visible); + /*virtual*/ BOOL handleAcceleratorKey(KEY key, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + /*virtual*/ BOOL handleJumpKey(KEY key); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - // rearrange the child rects so they fit the shape of the menu - // bar. - virtual void arrange( void ); - virtual void draw(); - virtual BOOL jumpKeysActive(); + /*virtual*/ void draw(); + /*virtual*/ BOOL jumpKeysActive(); // add a vertical separator to this menu - virtual BOOL appendSeparator( const std::string &separator_name = LLStringUtil::null ); - - // add a menu - this will create a drop down menu. - virtual BOOL appendMenu( LLMenuGL* menu ); + virtual BOOL addSeparator(); // LLView Functionality virtual BOOL handleHover( S32 x, S32 y, MASK mask ); @@ -730,6 +780,12 @@ public: void resetMenuTrigger() { mAltKeyTrigger = FALSE; } private: + // add a menu - this will create a drop down menu. + virtual BOOL appendMenu( LLMenuGL* menu ); + // rearrange the child rects so they fit the shape of the menu + // bar. + virtual void arrange( void ); + void checkMenuTrigger(); std::list mAccelerators; @@ -757,11 +813,20 @@ public: virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); + // Close context menus on right mouse up not handled by menus. + /*virtual*/ BOOL handleRightMouseUp( S32 x, S32 y, MASK mask ); + + virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); virtual const LLRect getMenuRect() const { return getLocalRect(); } - virtual BOOL hasVisibleMenu() const; + LLView*const getVisibleMenu() const; + virtual BOOL hasVisibleMenu() const {return getVisibleMenu() != NULL;} static void setActivatedItem(LLMenuItemGL* item); + // Need to detect if mouse-up after context menu spawn has moved. + // If not, need to keep the menu up. + static LLCoordGL sContextMenuSpawnPos; + private: static LLHandle sItemLastSelectedHandle; static LLFrameTimer sItemActivationTimer; @@ -805,7 +870,7 @@ private: class LLMenuItemTearOffGL : public LLMenuItemGL { public: - LLMenuItemTearOffGL( LLHandle parent_floater_handle = LLHandle()); + LLMenuItemTearOffGL(); virtual LLXMLNodePtr getXML(bool save_children = true) const; virtual std::string getType() const { return "tearoff_menu"; } @@ -814,8 +879,7 @@ public: virtual void draw(void); virtual U32 getNominalHeight() const; -private: - LLHandle mParentHandle; + LLFloater* getParentFloater(); }; diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index b8ebb3d6b..c3a0f5a6b 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -127,7 +127,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const std::string& name, const LLRect& rect &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLMultiSliderCtrl::onEditorGainFocus, this ); + mEditor->setFocusReceivedCallback( boost::bind(&LLMultiSliderCtrl::onFocusReceived, this) ); mEditor->setIgnoreTab(TRUE); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others @@ -151,16 +151,6 @@ LLMultiSliderCtrl::~LLMultiSliderCtrl() // Children all cleaned up by default view destructor. } -// static -void LLMultiSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) -{ - LLMultiSliderCtrl* self = (LLMultiSliderCtrl*) userdata; - llassert( caller == self->mEditor ); - - self->onFocusReceived(); -} - - void LLMultiSliderCtrl::setValue(const LLSD& value) { mMultiSlider->setValue(value); diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h index 47b1d1fb1..05e15178c 100644 --- a/indra/llui/llmultisliderctrl.h +++ b/indra/llui/llmultisliderctrl.h @@ -126,7 +126,6 @@ public: static void onSliderCommit(LLUICtrl* caller, void* userdata); static void onEditorCommit(LLUICtrl* ctrl, void* userdata); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); private: diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 9dc217e21..af0dc47c1 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -79,7 +79,6 @@ void LLPanel::init() mBorder = NULL; mDefaultBtn = NULL; setIsChrome(FALSE); //is this a decorator to a live window or a form? - mLastTabGroup = 0; mPanelHandle.bind(this); setTabStop(FALSE); @@ -269,20 +268,6 @@ void LLPanel::setDefaultBtn(const std::string& id) } } -void LLPanel::addCtrl( LLUICtrl* ctrl, S32 tab_group) -{ - mLastTabGroup = tab_group; - - LLView::addCtrl(ctrl, tab_group); -} - -void LLPanel::addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group) -{ - mLastTabGroup = tab_group; - - LLView::addCtrlAtEnd(ctrl, tab_group); -} - BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) { BOOL handled = FALSE; @@ -760,16 +745,6 @@ BOOL LLPanel::childHasFocus(const std::string& id) } } - -void LLPanel::childSetFocusChangedCallback(const std::string& id, void (*cb)(LLFocusableElement*, void*), void* user_data) -{ - LLUICtrl* child = getChild(id, true); - if (child) - { - child->setFocusChangedCallback(cb, user_data); - } -} - void LLPanel::childSetCommitCallback(const std::string& id, void (*cb)(LLUICtrl*, void*), void *userdata ) { LLUICtrl* child = getChild(id, true); @@ -1199,9 +1174,13 @@ void LLLayoutStack::draw() } } -void LLLayoutStack::removeCtrl(LLUICtrl* ctrl) +void LLLayoutStack::removeChild(LLView* ctrl) { - LLEmbeddedPanel* embedded_panelp = findEmbeddedPanel((LLPanel*)ctrl); + LLView::removeChild(ctrl); + LLPanel* panel = dynamic_cast(ctrl); + if(!panel) + return; + LLEmbeddedPanel* embedded_panelp = findEmbeddedPanel(panel); if (embedded_panelp) { @@ -1210,10 +1189,8 @@ void LLLayoutStack::removeCtrl(LLUICtrl* ctrl) } // need to update resizebars - + calcMinExtents(); - - LLView::removeCtrl(ctrl); } LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index f6373f71c..bb16b6e02 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -138,8 +138,6 @@ public: LLHandle getHandle() const { return mPanelHandle; } - S32 getLastTabGroup() const { return mLastTabGroup; } - const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); @@ -170,7 +168,6 @@ public: // LLUICtrl void childSetFocus(const std::string& id, BOOL focus = TRUE); BOOL childHasFocus(const std::string& id); - void childSetFocusChangedCallback(const std::string& id, void (*cb)(LLFocusableElement*, void*), void* user_data = NULL); void childSetCommitCallback(const std::string& id, void (*cb)(LLUICtrl*, void*), void* userdata = NULL ); void childSetDoubleClickCallback(const std::string& id, void (*cb)(void*), void* userdata = NULL ); @@ -236,10 +233,6 @@ private: // common construction logic void init(); - // From LLView - virtual void addCtrl( LLUICtrl* ctrl, S32 tab_group ); - virtual void addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group); - // Unified error reporting for the child* functions typedef std::set expected_members_list_t; mutable expected_members_list_t mExpectedMembers; @@ -254,7 +247,6 @@ private: LLViewBorder* mBorder; LLButton* mDefaultBtn; std::string mLabel; - S32 mLastTabGroup; LLRootHandle mPanelHandle; typedef std::map ui_string_map_t; @@ -279,7 +271,7 @@ public: /*virtual*/ void draw(); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ void removeCtrl(LLUICtrl* ctrl); + /*virtual*/ void removeChild(LLView* ctrl); static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp index 46b73f281..a0a9b36b9 100644 --- a/indra/llui/llresizebar.cpp +++ b/indra/llui/llresizebar.cpp @@ -185,7 +185,8 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) if (mSnappingEnabled) { - static LLCachedControl snap_margin (*LLUI::sConfigGroup,"SnapMargin", 0); + //static LLCachedControl snap_margin (*LLUI::sConfigGroup,"SnapMargin", 0); + S32 snap_margin = LLUI::sConfigGroup->getS32("SnapMargin"); switch( mSide ) { case LEFT: diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 1d25f3701..690f12b42 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -46,7 +46,7 @@ const S32 RESIZE_BORDER_WIDTH = 3; LLResizeHandle::LLResizeHandle( const std::string& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner ) : - LLView( name, rect, TRUE ), + LLView( name, rect, FALSE ), mDragLastScreenX( 0 ), mDragLastScreenY( 0 ), mLastMouseScreenX( 0 ), @@ -205,7 +205,8 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) LLView* snap_view = NULL; LLView* test_view = NULL; - static LLCachedControl snap_margin (*LLUI::sConfigGroup,"SnapMargin", 0); + //static LLCachedControl snap_margin (*LLUI::sConfigGroup,"SnapMargin", 0); + S32 snap_margin = LLUI::sConfigGroup->getS32("SnapMargin"); // now do snapping switch(mCorner) { diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 825a411c4..d41606636 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -73,7 +73,8 @@ LLScrollbar::LLScrollbar( mHighlightColor ( LLUI::sColorsGroup->getColor("DefaultHighlightLight") ), mShadowColor ( LLUI::sColorsGroup->getColor("DefaultShadowLight") ), mOnScrollEndCallback( NULL ), - mOnScrollEndData( NULL ) + mOnScrollEndData( NULL ), + mThickness( SCROLLBAR_SIZE ) { //llassert( 0 <= mDocSize ); //llassert( 0 <= mDocPos && mDocPos <= mDocSize ); @@ -92,22 +93,22 @@ LLScrollbar::LLScrollbar( if( LLScrollbar::VERTICAL == mOrientation ) { - line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), SCROLLBAR_SIZE, SCROLLBAR_SIZE ); + line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), mThickness, mThickness ); line_up_img="UIImgBtnScrollUpOutUUID"; line_up_selected_img="UIImgBtnScrollUpInUUID"; - line_down_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE ); + line_down_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); line_down_img="UIImgBtnScrollDownOutUUID"; line_down_selected_img="UIImgBtnScrollDownInUUID"; } else { // Horizontal - line_up_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE ); + line_up_rect.setOriginAndSize( 0, 0, mThickness, mThickness ); line_up_img="UIImgBtnScrollLeftOutUUID"; line_up_selected_img="UIImgBtnScrollLeftInUUID"; - line_down_rect.setOriginAndSize( getRect().getWidth() - SCROLLBAR_SIZE, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE ); + line_down_rect.setOriginAndSize( getRect().getWidth() - mThickness, 0, mThickness, mThickness ); line_down_img="UIImgBtnScrollRightOutUUID"; line_down_selected_img="UIImgBtnScrollRightInUUID"; } @@ -126,7 +127,7 @@ LLScrollbar::LLScrollbar( line_up_btn->setFollowsLeft(); line_up_btn->setFollowsBottom(); } - line_up_btn->setHeldDownCallback( &LLScrollbar::onLineUpBtnPressed ); + line_up_btn->setHeldDownCallback( boost::bind(&LLScrollbar::onLineUpBtnPressed, (void*)this) ); line_up_btn->setTabStop(FALSE); line_up_btn->setScaleImage(TRUE); @@ -137,7 +138,7 @@ LLScrollbar::LLScrollbar( &LLScrollbar::onLineDownBtnPressed, this, LLFontGL::getFontSansSerif() ); line_down_btn->setFollowsRight(); line_down_btn->setFollowsBottom(); - line_down_btn->setHeldDownCallback( &LLScrollbar::onLineDownBtnPressed ); + line_down_btn->setHeldDownCallback( boost::bind(&LLScrollbar::onLineDownBtnPressed, this) ); line_down_btn->setTabStop(FALSE); line_down_btn->setScaleImage(TRUE); addChild(line_down_btn); @@ -158,7 +159,8 @@ void LLScrollbar::setDocParams( S32 size, S32 pos ) updateThumbRect(); } -void LLScrollbar::setDocPos(S32 pos, BOOL update_thumb) +// returns true if document position really changed +bool LLScrollbar::setDocPos(S32 pos, BOOL update_thumb) { pos = llclamp(pos, 0, getDocPosMax()); if (pos != mDocPos) @@ -175,7 +177,9 @@ void LLScrollbar::setDocPos(S32 pos, BOOL update_thumb) { updateThumbRect(); } + return true; } + return false; } void LLScrollbar::setDocSize(S32 size) @@ -221,7 +225,7 @@ void LLScrollbar::updateThumbRect() const S32 THUMB_MIN_LENGTH = 16; S32 window_length = (mOrientation == LLScrollbar::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight(); - S32 thumb_bg_length = llmax(0, window_length - 2 * SCROLLBAR_SIZE); + S32 thumb_bg_length = llmax(0, window_length - 2 * mThickness); S32 visible_lines = llmin( mDocSize, mPageSize ); S32 thumb_length = mDocSize ? llmin(llmax( visible_lines * thumb_bg_length / mDocSize, THUMB_MIN_LENGTH), thumb_bg_length) : thumb_bg_length; @@ -229,24 +233,24 @@ void LLScrollbar::updateThumbRect() if( mOrientation == LLScrollbar::VERTICAL ) { - S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE; - S32 thumb_start_min = SCROLLBAR_SIZE + THUMB_MIN_LENGTH; + S32 thumb_start_max = thumb_bg_length + mThickness; + S32 thumb_start_min = mThickness + THUMB_MIN_LENGTH; S32 thumb_start = variable_lines ? llmin( llmax(thumb_start_max - (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_max; mThumbRect.mLeft = 0; mThumbRect.mTop = thumb_start; - mThumbRect.mRight = SCROLLBAR_SIZE; + mThumbRect.mRight = mThickness; mThumbRect.mBottom = thumb_start - thumb_length; } else { // Horizontal - S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE - thumb_length; - S32 thumb_start_min = SCROLLBAR_SIZE; + S32 thumb_start_max = thumb_bg_length + mThickness - thumb_length; + S32 thumb_start_min = mThickness; S32 thumb_start = variable_lines ? llmin(llmax( thumb_start_min + (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_min; mThumbRect.mLeft = thumb_start; - mThumbRect.mTop = SCROLLBAR_SIZE; + mThumbRect.mTop = mThickness; mThumbRect.mRight = thumb_start + thumb_length; mThumbRect.mBottom = 0; } @@ -318,21 +322,21 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) // S32 old_pos = mThumbRect.mTop; S32 delta_pixels = y - mDragStartY; - if( mOrigRect.mBottom + delta_pixels < SCROLLBAR_SIZE ) + if( mOrigRect.mBottom + delta_pixels < mThickness ) { - delta_pixels = SCROLLBAR_SIZE - mOrigRect.mBottom - 1; + delta_pixels = mThickness - mOrigRect.mBottom - 1; } else - if( mOrigRect.mTop + delta_pixels > height - SCROLLBAR_SIZE ) + if( mOrigRect.mTop + delta_pixels > height - mThickness ) { - delta_pixels = height - SCROLLBAR_SIZE - mOrigRect.mTop + 1; + delta_pixels = height - mThickness - mOrigRect.mTop + 1; } mThumbRect.mTop = mOrigRect.mTop + delta_pixels; mThumbRect.mBottom = mOrigRect.mBottom + delta_pixels; S32 thumb_length = mThumbRect.getHeight(); - S32 thumb_track_length = height - 2 * SCROLLBAR_SIZE; + S32 thumb_track_length = height - 2 * mThickness; if( delta_pixels != mLastDelta || mDocChanged) @@ -343,7 +347,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) { S32 variable_lines = getDocPosMax(); S32 pos = mThumbRect.mTop; - F32 ratio = F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length; + F32 ratio = F32(pos - mThickness - thumb_length) / usable_track_length; S32 new_pos = llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines ); // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly @@ -362,21 +366,21 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) S32 delta_pixels = x - mDragStartX; - if( mOrigRect.mLeft + delta_pixels < SCROLLBAR_SIZE ) + if( mOrigRect.mLeft + delta_pixels < mThickness ) { - delta_pixels = SCROLLBAR_SIZE - mOrigRect.mLeft - 1; + delta_pixels = mThickness - mOrigRect.mLeft - 1; } else - if( mOrigRect.mRight + delta_pixels > width - SCROLLBAR_SIZE ) + if( mOrigRect.mRight + delta_pixels > width - mThickness ) { - delta_pixels = width - SCROLLBAR_SIZE - mOrigRect.mRight + 1; + delta_pixels = width - mThickness - mOrigRect.mRight + 1; } mThumbRect.mLeft = mOrigRect.mLeft + delta_pixels; mThumbRect.mRight = mOrigRect.mRight + delta_pixels; S32 thumb_length = mThumbRect.getWidth(); - S32 thumb_track_length = width - 2 * SCROLLBAR_SIZE; + S32 thumb_track_length = width - 2 * mThickness; if( delta_pixels != mLastDelta || mDocChanged) { @@ -386,7 +390,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) { S32 variable_lines = getDocPosMax(); S32 pos = mThumbRect.mLeft; - F32 ratio = F32(pos - SCROLLBAR_SIZE) / usable_track_length; + F32 ratio = F32(pos - mThickness) / usable_track_length; S32 new_pos = llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines); @@ -405,7 +409,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) } else { - handled = childrenHandleMouseUp( x, y, mask ) != NULL; + handled = childrenHandleHover( x, y, mask ) != NULL; } // Opaque @@ -423,8 +427,8 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) { - changeLine( clicks * mStepSize, TRUE ); - return TRUE; + BOOL handled = changeLine( clicks * mStepSize, TRUE ); + return handled; } BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -475,14 +479,14 @@ void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent) if (mOrientation == VERTICAL) { - up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, SCROLLBAR_SIZE)); - down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, SCROLLBAR_SIZE)); + up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness)); + down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness)); up_button->setOrigin(up_button->getRect().mLeft, getRect().getHeight() - up_button->getRect().getHeight()); } else { - up_button->reshape(llmin(getRect().getWidth() / 2, SCROLLBAR_SIZE), up_button->getRect().getHeight()); - down_button->reshape(llmin(getRect().getWidth() / 2, SCROLLBAR_SIZE), down_button->getRect().getHeight()); + up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight()); + down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight()); down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), down_button->getRect().mBottom); } updateThumbRect(); @@ -513,10 +517,10 @@ void LLScrollbar::draw() if (!rounded_rect_imagep) { - gl_rect_2d(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0, - mOrientation == VERTICAL ? getRect().getHeight() - 2 * SCROLLBAR_SIZE : getRect().getHeight(), - mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * SCROLLBAR_SIZE : getRect().getWidth(), - mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0, mTrackColor, TRUE); + gl_rect_2d(mOrientation == HORIZONTAL ? mThickness : 0, + mOrientation == VERTICAL ? getRect().getHeight() - 2 * mThickness : getRect().getHeight(), + mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * mThickness : getRect().getWidth(), + mOrientation == VERTICAL ? mThickness : 0, mTrackColor, TRUE); gl_rect_2d(mThumbRect, mThumbColor, TRUE); @@ -560,9 +564,9 @@ void LLScrollbar::draw() } // end draw -void LLScrollbar::changeLine( S32 delta, BOOL update_thumb ) +bool LLScrollbar::changeLine( S32 delta, BOOL update_thumb ) { - setDocPos(mDocPos + delta, update_thumb); + return setDocPos(mDocPos + delta, update_thumb); } void LLScrollbar::setValue(const LLSD& value) diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 0bbf8662a..da13ff2af 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -82,7 +82,7 @@ public: // How many "lines" the "document" has scrolled. // 0 <= DocPos <= DocSize - DocVisibile - void setDocPos( S32 pos, BOOL update_thumb = TRUE ); + bool setDocPos( S32 pos, BOOL update_thumb = TRUE ); S32 getDocPos() const { return mDocPos; } BOOL isAtBeginning(); @@ -113,7 +113,7 @@ public: private: void updateThumbRect(); - void changeLine(S32 delta, BOOL update_thumb ); + bool changeLine(S32 delta, BOOL update_thumb ); void (*mChangeCallback)( S32 new_pos, LLScrollbar* self, void* userdata ); void* mCallbackUserData; @@ -142,6 +142,7 @@ private: void (*mOnScrollEndCallback)(void*); void *mOnScrollEndData; + S32 mThickness; }; diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 28890b199..8e740384a 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -69,52 +69,31 @@ LLScrollableContainerView::LLScrollableContainerView( const std::string& name, BOOL is_opaque, const LLColor4& bg_color ) : LLUICtrl( name, rect, FALSE, NULL, NULL ), - mScrolledView( scrolled_view ), - mIsOpaque( is_opaque ), - mBackgroundColor( bg_color ), - mReserveScrollCorner( FALSE ), mAutoScrolling( FALSE ), - mAutoScrollRate( 0.f ) + mAutoScrollRate( 0.f ), + mBackgroundColor( bg_color ), + mIsOpaque( is_opaque ), + mHideScrollbar( FALSE ), + mReserveScrollCorner( FALSE ), + mMinAutoScrollRate( MIN_AUTO_SCROLL_RATE ), + mMaxAutoScrollRate( MAX_AUTO_SCROLL_RATE ), + mScrolledView( scrolled_view ) { if( mScrolledView ) { - addChild( mScrolledView ); + LLView::addChild( mScrolledView ); } - init(); -} - -// LLUICtrl constructor -LLScrollableContainerView::LLScrollableContainerView( const std::string& name, const LLRect& rect, - LLUICtrl* scrolled_ctrl, BOOL is_opaque, - const LLColor4& bg_color) : - LLUICtrl( name, rect, FALSE, NULL, NULL ), - mScrolledView( scrolled_ctrl ), - mIsOpaque( is_opaque ), - mBackgroundColor( bg_color ), - mReserveScrollCorner( FALSE ), - mAutoScrolling( FALSE ), - mAutoScrollRate( 0.f ) -{ - if( scrolled_ctrl ) - { - addChild( scrolled_ctrl ); - } - - init(); -} - -void LLScrollableContainerView::init() -{ + S32 scrollbar_size = SCROLLBAR_SIZE; LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 ); mBorder = new LLViewBorder( std::string("scroll border"), border_rect, LLViewBorder::BEVEL_IN ); - addChild( mBorder ); + LLView::addChild( mBorder ); mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - mInnerRect.stretch( -mBorder->getBorderWidth() ); + mInnerRect.stretch( -getBorderWidth() ); LLRect vertical_scroll_rect = mInnerRect; - vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - SCROLLBAR_SIZE; + vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size; mScrollbar[VERTICAL] = new LLScrollbar( std::string("scrollable vertical"), vertical_scroll_rect, LLScrollbar::VERTICAL, @@ -123,14 +102,14 @@ void LLScrollableContainerView::init() mInnerRect.getHeight(), NULL, this, VERTICAL_MULTIPLE); - addChild( mScrollbar[VERTICAL] ); + LLView::addChild( mScrollbar[VERTICAL] ); mScrollbar[VERTICAL]->setVisible( FALSE ); mScrollbar[VERTICAL]->setFollowsRight(); mScrollbar[VERTICAL]->setFollowsTop(); mScrollbar[VERTICAL]->setFollowsBottom(); LLRect horizontal_scroll_rect = mInnerRect; - horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + SCROLLBAR_SIZE; + horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size; mScrollbar[HORIZONTAL] = new LLScrollbar( std::string("scrollable horizontal"), horizontal_scroll_rect, LLScrollbar::HORIZONTAL, @@ -139,7 +118,7 @@ void LLScrollableContainerView::init() mInnerRect.getWidth(), NULL, this, HORIZONTAL_MULTIPLE); - addChild( mScrollbar[HORIZONTAL] ); + LLView::addChild( mScrollbar[HORIZONTAL] ); mScrollbar[HORIZONTAL]->setVisible( FALSE ); mScrollbar[HORIZONTAL]->setFollowsLeft(); mScrollbar[HORIZONTAL]->setFollowsRight(); @@ -190,8 +169,8 @@ void LLScrollableContainerView::reshape(S32 width, S32 height, { LLUICtrl::reshape( width, height, called_from_parent ); - mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - mInnerRect.stretch( -mBorder->getBorderWidth() ); + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); if (mScrolledView) { @@ -201,22 +180,33 @@ void LLScrollableContainerView::reshape(S32 width, S32 height, S32 visible_height = 0; BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); mScrollbar[VERTICAL]->setPageSize( visible_height ); mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); mScrollbar[HORIZONTAL]->setPageSize( visible_width ); + updateScroll(); } } BOOL LLScrollableContainerView::handleKeyHere(KEY key, MASK mask) { + // allow scrolled view to handle keystrokes in case it delegated keyboard focus + // to the scroll container. + // NOTE: this should not recurse indefinitely as handleKeyHere + // should not propagate to parent controls, so mScrolledView should *not* + // call LLScrollContainer::handleKeyHere in turn + if (mScrolledView && mScrolledView->handleKeyHere(key, mask)) + { + return TRUE; + } for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) { if( mScrollbar[i]->handleKeyHere(key, mask) ) { + updateScroll(); return TRUE; } } @@ -226,38 +216,37 @@ BOOL LLScrollableContainerView::handleKeyHere(KEY key, MASK mask) BOOL LLScrollableContainerView::handleScrollWheel( S32 x, S32 y, S32 clicks ) { - for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) - { - // Note: tries vertical and then horizontal + // Give event to my child views - they may have scroll bars + // (Bad UI design, but technically possible.) + if (LLUICtrl::handleScrollWheel(x,y,clicks)) + return TRUE; + // When the vertical scrollbar is visible, scroll wheel + // only affects vertical scrolling. It's confusing to have + // scroll wheel perform both vertical and horizontal in a + // single container. + LLScrollbar* vertical = mScrollbar[VERTICAL]; + if (vertical->getVisible() + && vertical->getEnabled()) + { // Pretend the mouse is over the scrollbar - if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) ) + if (vertical->handleScrollWheel( 0, 0, clicks ) ) { - return TRUE; + updateScroll(); } + // Always eat the event + return TRUE; } - // Eat scroll wheel event (to avoid scrolling nested containers?) - return TRUE; -} - -BOOL LLScrollableContainerView::needsToScroll(S32 x, S32 y, LLScrollableContainerView::SCROLL_ORIENTATION axis) const -{ - if(mScrollbar[axis]->getVisible()) + LLScrollbar* horizontal = mScrollbar[HORIZONTAL]; + // Test enablement and visibility for consistency with + // LLView::childrenHandleScrollWheel(). + if (horizontal->getVisible() + && horizontal->getEnabled() + && horizontal->handleScrollWheel( 0, 0, clicks ) ) { - LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); - const S32 AUTOSCROLL_SIZE = 10; - if(mScrollbar[axis]->getVisible()) - { - inner_rect_local.mRight -= SCROLLBAR_SIZE; - inner_rect_local.mTop += AUTOSCROLL_SIZE; - inner_rect_local.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE; - } - if( inner_rect_local.pointInRect( x, y ) && (mScrollbar[axis]->getDocPos() > 0) ) - { - return TRUE; - } - + updateScroll(); + return TRUE; } return FALSE; } @@ -269,65 +258,10 @@ BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask, EAcceptance* accept, std::string& tooltip_msg) { + //S32 scrollbar_size = SCROLLBAR_SIZE; // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; - BOOL handled = FALSE; - if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() ) - { - const S32 AUTOSCROLL_SIZE = 10; - S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); - - LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); - if( mScrollbar[HORIZONTAL]->getVisible() ) - { - inner_rect_local.mBottom += SCROLLBAR_SIZE; - } - if( mScrollbar[VERTICAL]->getVisible() ) - { - inner_rect_local.mRight -= SCROLLBAR_SIZE; - } - - if( mScrollbar[HORIZONTAL]->getVisible() ) - { - LLRect left_scroll_rect = inner_rect_local; - left_scroll_rect.mRight = AUTOSCROLL_SIZE; - if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) ) - { - mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed ); - mAutoScrolling = TRUE; - handled = TRUE; - } - - LLRect right_scroll_rect = inner_rect_local; - right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE; - if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) ) - { - mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed ); - mAutoScrolling = TRUE; - handled = TRUE; - } - } - if( mScrollbar[VERTICAL]->getVisible() ) - { - LLRect bottom_scroll_rect = inner_rect_local; - bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom; - if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) ) - { - mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed ); - mAutoScrolling = TRUE; - handled = TRUE; - } - - LLRect top_scroll_rect = inner_rect_local; - top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE; - if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) ) - { - mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed ); - mAutoScrolling = TRUE; - handled = TRUE; - } - } - } + BOOL handled = autoScroll(x, y); if( !handled ) { @@ -338,6 +272,78 @@ BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask, return TRUE; } +bool LLScrollableContainerView::autoScroll(S32 x, S32 y) +{ + S32 scrollbar_size = SCROLLBAR_SIZE; + + bool scrolling = false; + if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() ) + { + LLRect screen_local_extents; + screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents); + + LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); + if( mScrollbar[HORIZONTAL]->getVisible() ) + { + inner_rect_local.mBottom += scrollbar_size; + } + if( mScrollbar[VERTICAL]->getVisible() ) + { + inner_rect_local.mRight -= scrollbar_size; + } + + // clip rect against root view + inner_rect_local.intersectWith(screen_local_extents); + + S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); + // autoscroll region should take up no more than one third of visible scroller area + S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10); + S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10); + + if( mScrollbar[HORIZONTAL]->getVisible() ) + { + LLRect left_scroll_rect = screen_local_extents; + left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width; + if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) ) + { + mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed ); + mAutoScrolling = TRUE; + scrolling = true; + } + + LLRect right_scroll_rect = screen_local_extents; + right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width; + if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) ) + { + mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed ); + mAutoScrolling = TRUE; + scrolling = true; + } + } + if( mScrollbar[VERTICAL]->getVisible() ) + { + LLRect bottom_scroll_rect = screen_local_extents; + bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height; + if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) ) + { + mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed ); + mAutoScrolling = TRUE; + scrolling = true; + } + + LLRect top_scroll_rect = screen_local_extents; + top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height; + if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) ) + { + mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed ); + mAutoScrolling = TRUE; + scrolling = true; + } + } + } + return scrolling; +} + BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect) { @@ -368,42 +374,44 @@ BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, std::string& msg, LL void LLScrollableContainerView::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const { - const LLRect& rect = mScrolledView->getRect(); - calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar); -} - -void LLScrollableContainerView::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const -{ + const LLRect& doc_rect = getScrolledViewRect(); + S32 scrollbar_size = SCROLLBAR_SIZE; S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); - *visible_width = getRect().getWidth() - 2 * mBorder->getBorderWidth(); - *visible_height = getRect().getHeight() - 2 * mBorder->getBorderWidth(); + S32 border_width = getBorderWidth(); + *visible_width = getRect().getWidth() - 2 * border_width; + *visible_height = getRect().getHeight() - 2 * border_width; *show_v_scrollbar = FALSE; - if( *visible_height < doc_height ) - { - *show_v_scrollbar = TRUE; - *visible_width -= SCROLLBAR_SIZE; - } - *show_h_scrollbar = FALSE; - if( *visible_width < doc_width ) - { - *show_h_scrollbar = TRUE; - *visible_height -= SCROLLBAR_SIZE; - // Must retest now that visible_height has changed - if( !*show_v_scrollbar && (*visible_height < doc_height) ) + if (!mHideScrollbar) + { + if( *visible_height < doc_height ) { *show_v_scrollbar = TRUE; - *visible_width -= SCROLLBAR_SIZE; + *visible_width -= scrollbar_size; + } + + if( *visible_width < doc_width ) + { + *show_h_scrollbar = TRUE; + *visible_height -= scrollbar_size; + + // Must retest now that visible_height has changed + if( !*show_v_scrollbar && (*visible_height < doc_height) ) + { + *show_v_scrollbar = TRUE; + *visible_width -= scrollbar_size; + } } } } void LLScrollableContainerView::draw() { + S32 scrollbar_size = SCROLLBAR_SIZE; if (mAutoScrolling) { // add acceleration to autoscroll @@ -411,76 +419,79 @@ void LLScrollableContainerView::draw() } else { - // reset to minimum - mAutoScrollRate = MIN_AUTO_SCROLL_RATE; + // reset to minimum for next time + mAutoScrollRate = mMinAutoScrollRate; } - // clear this flag to be set on next call to handleDragAndDrop + // clear this flag to be set on next call to autoScroll mAutoScrolling = FALSE; // auto-focus when scrollbar active // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc) - if (!gFocusMgr.childHasKeyboardFocus(this) && - (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) + if (!hasFocus() + && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) { focusFirstItem(); } - // Draw background - if( mIsOpaque ) + if (getRect().isValid()) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4fv( mBackgroundColor.mV ); - gl_rect_2d( mInnerRect ); - } + // Draw background + if( mIsOpaque ) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4fv( mBackgroundColor.mV ); + gl_rect_2d( mInnerRect ); + } - // Draw mScrolledViews and update scroll bars. - // get a scissor region ready, and draw the scrolling view. The - // scissor region ensures that we don't draw outside of the bounds - // of the rectangle. - if( mScrolledView ) - { - updateScroll(); - - // Draw the scrolled area. + // Draw mScrolledViews and update scroll bars. + // get a scissor region ready, and draw the scrolling view. The + // scissor region ensures that we don't draw outside of the bounds + // of the rectangle. + if( mScrolledView ) { - S32 visible_width = 0; - S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; - BOOL show_h_scrollbar = FALSE; - calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + updateScroll(); - LLLocalClipRect clip(LLRect(mInnerRect.mLeft, - mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) + visible_height, - visible_width, - mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) - )); - drawChild(mScrolledView); + // Draw the scrolled area. + { + S32 visible_width = 0; + S32 visible_height = 0; + BOOL show_v_scrollbar = FALSE; + BOOL show_h_scrollbar = FALSE; + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + + LLLocalClipRect clip(LLRect(mInnerRect.mLeft, + mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height, + mInnerRect.mRight - (show_v_scrollbar ? scrollbar_size: 0), + mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + )); + drawChild(mScrolledView); + } } - } - // Highlight border if a child of this container has keyboard focus - if( mBorder->getVisible() ) - { - mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) ); - } + // Highlight border if a child of this container has keyboard focus + if( mBorder->getVisible() ) + { + mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) ); + } - // Draw all children except mScrolledView - // Note: scrollbars have been adjusted by above drawing code - for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin(); - child_iter != getChildList()->rend(); ++child_iter) - { - LLView *viewp = *child_iter; - if( sDebugRects ) + // Draw all children except mScrolledView + // Note: scrollbars have been adjusted by above drawing code + for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin(); + child_iter != getChildList()->rend(); ++child_iter) { - sDepth++; - } - if( (viewp != mScrolledView) && viewp->getVisible() ) - { - drawChild(viewp); - } - if( sDebugRects ) - { - sDepth--; + LLView *viewp = *child_iter; + if( sDebugRects ) + { + sDepth++; + } + if( (viewp != mScrolledView) && viewp->getVisible() ) + { + drawChild(viewp); + } + if( sDebugRects ) + { + sDepth--; + } } } @@ -491,8 +502,30 @@ void LLScrollableContainerView::draw() } // end draw +bool LLScrollableContainerView::addChild(LLView* view, S32 tab_group) +{ + if (!mScrolledView) + { + // Use the first panel or container as the scrollable view (bit of a hack) + mScrolledView = view; + } + + bool ret_val = LLView::addChild(view, tab_group); + + //bring the scrollbars to the front + sendChildToFront( mScrollbar[HORIZONTAL] ); + sendChildToFront( mScrollbar[VERTICAL] ); + + return ret_val; +} + void LLScrollableContainerView::updateScroll() { + if (!mScrolledView) + { + return; + } + S32 scrollbar_size = SCROLLBAR_SIZE; LLRect doc_rect = mScrolledView->getRect(); S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); @@ -500,9 +533,9 @@ void LLScrollableContainerView::updateScroll() S32 visible_height = 0; BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); - S32 border_width = mBorder->getBorderWidth(); + S32 border_width = getBorderWidth(); if( show_v_scrollbar ) { if( doc_rect.mTop < getRect().getHeight() - border_width ) @@ -516,15 +549,15 @@ void LLScrollableContainerView::updateScroll() S32 v_scrollbar_height = visible_height; if( !show_h_scrollbar && mReserveScrollCorner ) { - v_scrollbar_height -= SCROLLBAR_SIZE; + v_scrollbar_height -= scrollbar_size; } - mScrollbar[VERTICAL]->reshape( SCROLLBAR_SIZE, v_scrollbar_height, TRUE ); + mScrollbar[VERTICAL]->reshape( scrollbar_size, v_scrollbar_height, TRUE ); // Make room for the horizontal scrollbar (or not) S32 v_scrollbar_offset = 0; if( show_h_scrollbar || mReserveScrollCorner ) { - v_scrollbar_offset = SCROLLBAR_SIZE; + v_scrollbar_offset = scrollbar_size; } LLRect r = mScrollbar[VERTICAL]->getRect(); r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset ); @@ -554,9 +587,9 @@ void LLScrollableContainerView::updateScroll() S32 h_scrollbar_width = visible_width; if( !show_v_scrollbar && mReserveScrollCorner ) { - h_scrollbar_width -= SCROLLBAR_SIZE; + h_scrollbar_width -= scrollbar_size; } - mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, SCROLLBAR_SIZE, TRUE ); + mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, TRUE ); } else { @@ -576,101 +609,115 @@ void LLScrollableContainerView::updateScroll() void LLScrollableContainerView::setBorderVisible(BOOL b) { mBorder->setVisible( b ); + // Recompute inner rect, as border visibility changes it + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); } -// Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled) -void LLScrollableContainerView::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset) +LLRect LLScrollableContainerView::getVisibleContentRect() +{ + updateScroll(); + LLRect visible_rect = getContentWindowRect(); + LLRect contents_rect = mScrolledView->getRect(); + visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom); + return visible_rect; +} +LLRect LLScrollableContainerView::getContentWindowRect() +{ + updateScroll(); + LLRect scroller_view_rect; + S32 visible_width = 0; + S32 visible_height = 0; + BOOL show_h_scrollbar = FALSE; + BOOL show_v_scrollbar = FALSE; + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + S32 border_width = getBorderWidth(); + scroller_view_rect.setOriginAndSize(border_width, + show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width, + visible_width, + visible_height); + return scroller_view_rect; +} + +// rect is in document coordinates, constraint is in display coordinates relative to content window rect +void LLScrollableContainerView::scrollToShowRect(const LLRect& rect, const LLRect& constraint) { if (!mScrolledView) { - llwarns << "LLScrollableContainerView::scrollToShowRect with no view!" << llendl; + llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl; return; } - S32 visible_width = 0; - S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; - BOOL show_h_scrollbar = FALSE; - const LLRect& scrolled_rect = mScrolledView->getRect(); - calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + LLRect content_window_rect = getContentWindowRect(); + // get document rect + LLRect scrolled_rect = mScrolledView->getRect(); - // can't be so far left that right side of rect goes off screen, or so far right that left side does - S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0); - // can't be so high that bottom of rect goes off screen, or so low that top does - S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight())); + // shrink target rect to fit within constraint region, biasing towards top left + LLRect rect_to_constrain = rect; + rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight()); + rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth()); - // Vertical - // 1. First make sure the top is visible - // 2. Then, if possible without hiding the top, make the bottom visible. - S32 vert_pos = mScrollbar[VERTICAL]->getDocPos(); + // calculate allowable positions for scroller window in document coordinates + LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight, + rect_to_constrain.mBottom - constraint.mBottom, + rect_to_constrain.mLeft - constraint.mLeft, + rect_to_constrain.mTop - constraint.mTop); - // find scrollbar position to get top of rect on screen (scrolling up) - S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset; - // find scrollbar position to get bottom of rect on screen (scrolling down) - S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset; - // scroll up far enough to see top or scroll down just enough if item is bigger than visual area - if( vert_pos >= top_offset || visible_height < rect.getHeight()) - { - vert_pos = top_offset; - } - // else scroll down far enough to see bottom - else - if( vert_pos <= bottom_offset ) - { - vert_pos = bottom_offset; - } + // translate from allowable region for lower left corner to upper left corner + allowable_scroll_rect.translate(0, content_window_rect.getHeight()); + + S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(), + mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll + mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); - mScrollbar[VERTICAL]->setPageSize( visible_height ); + mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() ); mScrollbar[VERTICAL]->setDocPos( vert_pos ); - // Horizontal - // 1. First make sure left side is visible - // 2. Then, if possible without hiding the left side, make the right side visible. - S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos(); - S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset; - S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset; + S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(), + allowable_scroll_rect.mLeft, + allowable_scroll_rect.mRight); - if( horiz_pos >= left_offset || visible_width < rect.getWidth() ) - { - horiz_pos = left_offset; - } - else if( horiz_pos <= right_offset ) - { - horiz_pos = right_offset; - } - mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); - mScrollbar[HORIZONTAL]->setPageSize( visible_width ); - mScrollbar[HORIZONTAL]->setDocPos( horiz_pos ); + mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() ); + mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos ); // propagate scroll to document updateScroll(); + + // In case we are in accordion tab notify parent to show selected rectangle + LLRect screen_rc; + localRectToScreen(rect_to_constrain, &screen_rc); + notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); } void LLScrollableContainerView::pageUp(S32 overlap) { mScrollbar[VERTICAL]->pageUp(overlap); + updateScroll(); } void LLScrollableContainerView::pageDown(S32 overlap) { mScrollbar[VERTICAL]->pageDown(overlap); + updateScroll(); } void LLScrollableContainerView::goToTop() { mScrollbar[VERTICAL]->setDocPos(0); + updateScroll(); } void LLScrollableContainerView::goToBottom() { mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize()); + updateScroll(); } S32 LLScrollableContainerView::getBorderWidth() const { - if (mBorder) + if (mBorder && mBorder->getVisible()) { return mBorder->getBorderWidth(); } diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index 70fc9087d..a5002a341 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -63,30 +63,29 @@ public: LLScrollableContainerView( const std::string& name, const LLRect& rect, LLView* scrolled_view, BOOL is_opaque = FALSE, const LLColor4& bg_color = LLColor4(0,0,0,0) ); - LLScrollableContainerView( const std::string& name, const LLRect& rect, - LLUICtrl* scrolled_ctrl, BOOL is_opaque = FALSE, - const LLColor4& bg_color = LLColor4(0,0,0,0) ); virtual ~LLScrollableContainerView( void ); void setScrolledView(LLView* view) { mScrolledView = view; } virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } - void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; - void calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; void setBorderVisible( BOOL b ); - void scrollToShowRect( const LLRect& rect, const LLCoordGL& desired_offset ); + void scrollToShowRect( const LLRect& rect, const LLRect& constraint); + void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); } + void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; } - const LLRect& getScrolledViewRect() const { return mScrolledView->getRect(); } + LLRect getVisibleContentRect(); + LLRect getContentWindowRect(); + const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; } void pageUp(S32 overlap = 0); void pageDown(S32 overlap = 0); void goToTop(); void goToBottom(); + bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); } + bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); } S32 getBorderWidth() const; - BOOL needsToScroll(S32 x, S32 y, SCROLL_ORIENTATION axis) const; - // LLView functionality virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual BOOL handleKeyHere(KEY key, MASK mask); @@ -99,8 +98,10 @@ public: virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect); virtual void draw(); - - virtual LLXMLNodePtr getXML(bool save_children = true) const; + virtual bool addChild(LLView* view, S32 tab_group = 0); + + bool autoScroll(S32 x, S32 y); + virtual LLXMLNodePtr getXML(bool save_children) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); private: @@ -110,6 +111,9 @@ private: virtual void scrollHorizontal( S32 new_pos ); virtual void scrollVertical( S32 new_pos ); void updateScroll(); +public: + void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; +private: LLScrollbar* mScrollbar[SCROLLBAR_COUNT]; LLView* mScrolledView; @@ -121,6 +125,9 @@ private: BOOL mReserveScrollCorner; BOOL mAutoScrolling; F32 mAutoScrollRate; + F32 mMinAutoScrollRate; + F32 mMaxAutoScrollRate; + bool mHideScrollbar; }; diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index c2afb78f7..8aa8b9fe3 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -52,7 +52,7 @@ void LLScrollingPanelList::clearPanels() void LLScrollingPanelList::addPanel( LLScrollingPanel* panel ) { - addChildAtEnd( panel ); + addChildInBack( panel ); mPanelList.push_front( panel ); const S32 GAP_BETWEEN_PANELS = 6; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 01c3a4731..8f0cc0c64 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -117,7 +117,11 @@ LLScrollListIcon::LLScrollListIcon(LLUIImagePtr icon, S32 width) LLScrollListIcon::LLScrollListIcon(const LLSD& value, S32 width) : LLScrollListCell(width), - mColor(LLColor4::white) + // + mCallback(NULL), + mUserData(NULL), + // + mColor(LLColor4::white) { setValue(value); } @@ -637,6 +641,7 @@ LLScrollListCtrl::LLScrollListCtrl(const std::string& name, const LLRect& rect, : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data), mLineHeight(0), mScrollLines(0), + mMouseWheelOpaque(true), mPageLines(0), mHeadingHeight(20), mMaxSelectable(0), @@ -745,11 +750,6 @@ S32 LLScrollListCtrl::getSearchColumn() LLScrollListCtrl::~LLScrollListCtrl() { std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } } @@ -1951,6 +1951,12 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) BOOL handled = FALSE; // Pretend the mouse is over the scrollbar handled = mScrollbar->handleScrollWheel( 0, 0, clicks ); + + if (mMouseWheelOpaque) + { + return TRUE; + } + return handled; } @@ -2129,6 +2135,8 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) mSelectionChanged = FALSE; handleClick(x, y, mask); + + setFocus(TRUE); } return TRUE; @@ -2843,6 +2851,8 @@ LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const node->createChild("draw_stripes", TRUE)->setBoolValue(mDrawStripes); node->createChild("column_padding", TRUE)->setIntValue(mColumnPadding); + + node->createChild("mouse_wheel_opaque", TRUE)->setBoolValue(mMouseWheelOpaque); addColorXML(node, mBgWriteableColor, "bg_writeable_color", "ScrollBgWriteableColor"); addColorXML(node, mBgReadOnlyColor, "bg_read_only_color", "ScrollBgReadOnlyColor"); @@ -2970,6 +2980,9 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac BOOL sort_ascending = TRUE; node->getAttributeBOOL("sort_ascending", sort_ascending); + + BOOL mouse_wheel_opaque = TRUE; + node->getAttributeBOOL("mouse_wheel_opaque", mouse_wheel_opaque); LLUICtrlCallback callback = NULL; @@ -2994,6 +3007,8 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac scroll_list->initFromXML(node, parent); scroll_list->setSearchColumn(search_column); + + scroll_list->mMouseWheelOpaque = mouse_wheel_opaque; LLSD columns; S32 index = 0; @@ -3684,9 +3699,9 @@ LLColumnHeader::LLColumnHeader(const std::string& label, const LLRect &rect, LLS mButton->setTabStop(FALSE); // require at least two frames between mouse down and mouse up event to capture intentional "hold" not just bad framerate mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2); - mButton->setHeldDownCallback(onHeldDown); - mButton->setClickedCallback(onClick); - mButton->setMouseDownCallback(onMouseDown); + mButton->setHeldDownCallback(boost::bind(&LLColumnHeader::onHeldDown, this)); + mButton->setClickedCallback(boost::bind(&LLColumnHeader::onClick, this)); + mButton->setMouseDownCallback(boost::bind(&LLColumnHeader::onMouseDown, this)); mButton->setCallbackUserData(this); mButton->setToolTip(label); @@ -3848,8 +3863,8 @@ void LLColumnHeader::setImage(const std::string &image_name) { if (mButton) { - mButton->setImageSelected(image_name); - mButton->setImageUnselected(image_name); + mButton->setImageSelected(LLUI::getUIImage(image_name)); + mButton->setImageUnselected(LLUI::getUIImage(image_name)); } } @@ -3885,6 +3900,8 @@ void LLColumnHeader::onClick(void* user_data) // propagate new sort order to sort order list headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1); + + headerp->mList->setFocus(TRUE); } //static diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 10b7e13a2..ada8032f1 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -671,6 +671,7 @@ private: BOOL mCommitOnSelectionChange; BOOL mSelectionChanged; BOOL mNeedsScroll; + BOOL mMouseWheelOpaque; BOOL mCanSelect; BOOL mDisplayColumnHeaders; BOOL mColumnsDirty; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 60907fd58..41c4c0560 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -121,7 +121,7 @@ LLSliderCtrl::LLSliderCtrl(const std::string& name, const LLRect& rect, &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus, this ); + mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onFocusReceived, this) ); mEditor->setIgnoreTab(TRUE); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others @@ -140,17 +140,6 @@ LLSliderCtrl::LLSliderCtrl(const std::string& name, const LLRect& rect, updateText(); } - -// static -void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) -{ - LLSliderCtrl* self = (LLSliderCtrl*) userdata; - llassert( caller == self->mEditor ); - - self->onFocusReceived(); -} - - void LLSliderCtrl::setValue(F32 v, BOOL from_event) { mSlider->setValue( v, from_event ); diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index 68748eb4d..f7c321235 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -117,7 +117,6 @@ public: static void onSliderCommit(LLUICtrl* caller, void* userdata); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); private: diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index ce13a2697..4db05f0cc 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -101,7 +101,7 @@ LLSpinCtrl::LLSpinCtrl( const std::string& name, const LLRect& rect, const std:: &LLSpinCtrl::onUpBtn, this, LLFontGL::getFontSansSerif() ); mUpBtn->setFollowsLeft(); mUpBtn->setFollowsBottom(); - mUpBtn->setHeldDownCallback( &LLSpinCtrl::onUpBtn ); + mUpBtn->setHeldDownCallback(boost::bind(&LLSpinCtrl::onUpBtn,this)); mUpBtn->setTabStop(FALSE); addChild(mUpBtn); @@ -115,7 +115,7 @@ LLSpinCtrl::LLSpinCtrl( const std::string& name, const LLRect& rect, const std:: &LLSpinCtrl::onDownBtn, this, LLFontGL::getFontSansSerif() ); mDownBtn->setFollowsLeft(); mDownBtn->setFollowsBottom(); - mDownBtn->setHeldDownCallback( &LLSpinCtrl::onDownBtn ); + mDownBtn->setHeldDownCallback(boost::bind(&LLSpinCtrl::onDownBtn,this)); mDownBtn->setTabStop(FALSE); addChild(mDownBtn); @@ -126,7 +126,7 @@ LLSpinCtrl::LLSpinCtrl( const std::string& name, const LLRect& rect, const std:: &LLLineEditor::prevalidateASCII ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus, this ); + mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onFocusReceived, this) ); //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand @@ -243,15 +243,6 @@ void LLSpinCtrl::onDownBtn( void *userdata ) } } -// static -void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) -{ - LLSpinCtrl* self = (LLSpinCtrl*) userdata; - llassert( caller == self->mEditor ); - - self->onFocusReceived(); -} - void LLSpinCtrl::setValue(const LLSD& value ) { F32 v = (F32)value.asReal(); diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index b9d94e57c..9e53c362f 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -110,7 +110,6 @@ public: virtual void draw(); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); static void onUpBtn(void *userdata); diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index eeff21cdf..0f08f2c58 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -803,7 +803,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, LLStringUtil::null, LLStringUtil::null, LLStringUtil::null, - &LLTabContainer::onTabBtn, NULL, + NULL, NULL, font, trimmed_label, trimmed_label); btn->setImages(std::string("tab_left.tga"), std::string("tab_left_selected.tga")); @@ -825,7 +825,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, btn = new LLButton(std::string(child->getName()) + " tab", btn_rect, LLStringUtil::null, LLStringUtil::null, LLStringUtil::null, - &LLTabContainer::onTabBtn, NULL, // set userdata below + NULL, NULL, // set userdata below font, trimmed_label, trimmed_label ); btn->setVisible( FALSE ); @@ -865,7 +865,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, if (btn) { btn->setSaveToXML(false); - btn->setCallbackUserData( tuple ); + btn->setClickedCallback(&LLTabContainer::onTabBtn, tuple); addChild( btn, 0 ); } if (child) @@ -1682,7 +1682,7 @@ void LLTabContainer::initButtons() mPrevArrowBtn = new LLButton(std::string("Left Arrow"), left_arrow_btn_rect, out_id, in_id, LLStringUtil::null, &LLTabContainer::onPrevBtn, this, LLFontGL::getFontSansSerif() ); - mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld); + mPrevArrowBtn->setHeldDownCallback(boost::bind(LLTabContainer::onPrevBtnHeld, this)); mPrevArrowBtn->setFollowsLeft(); out_id = "UIImgBtnJumpRightOutUUID"; @@ -1717,12 +1717,12 @@ void LLTabContainer::initButtons() } } - mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld); + mPrevArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onPrevBtnHeld, this)); mPrevArrowBtn->setSaveToXML(false); mPrevArrowBtn->setTabStop(FALSE); addChild(mPrevArrowBtn); - mNextArrowBtn->setHeldDownCallback(onNextBtnHeld); + mNextArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onNextBtnHeld, this)); mNextArrowBtn->setSaveToXML(false); mNextArrowBtn->setTabStop(FALSE); addChild(mNextArrowBtn); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index cd9d06c58..4bc9ee1fc 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -356,12 +356,12 @@ LLTextEditor::LLTextEditor( { menu = new LLMenuGL(LLStringUtil::null); }*/ - menu->append(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); - menu->append(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); - menu->append(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); - menu->append(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); - menu->append(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); - menu->appendSeparator("Spelsep"); + menu->addChild(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); + menu->addSeparator(); menu->setCanTearOff(FALSE); menu->setVisible(FALSE); mPopupMenuHandle = menu->getHandle(); @@ -372,18 +372,12 @@ LLTextEditor::~LLTextEditor() { gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() - // Route menu back to the default - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - // Scrollbar is deleted by LLView mHoverSegment = NULL; std::for_each(mSegments.begin(), mSegments.end(), DeletePointer()); std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - LLView::deleteViewByHandle(mPopupMenuHandle); + //LLView::deleteViewByHandle(mPopupMenuHandle); } void LLTextEditor::context_cut(void* data) { @@ -1283,7 +1277,8 @@ BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_ BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) { // Pretend the mouse is over the scrollbar - return mScrollbar->handleScrollWheel( 0, 0, clicks ); + mScrollbar->handleScrollWheel( 0, 0, clicks ); + return TRUE; } BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) @@ -1389,7 +1384,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) SpellMenuBind * tempBind = suggestionMenuItems[i]; if(tempBind) { - menu->remove(tempBind->menuItem); + menu->removeChild(tempBind->menuItem); tempBind->menuItem->die(); //delete tempBind->menuItem; //tempBind->menuItem = NULL; @@ -1425,7 +1420,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) tempStruct->word, spell_correct, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); + menu->addChild(suggMenuItem); } SpellMenuBind * tempStruct = new SpellMenuBind; tempStruct->origin = this; @@ -1437,7 +1432,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) "Add Word", spell_add, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); + menu->addChild(suggMenuItem); } } @@ -1456,7 +1451,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) tempStruct->word, spell_show, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; suggestionMenuItems.push_back(tempStruct); - menu->append(suggMenuItem); + menu->addChild(suggMenuItem); } mLastContextMenuX = x; mLastContextMenuY = y; @@ -3246,7 +3241,7 @@ void LLTextEditor::drawCursor() } // Make sure the IME is in the right place - LLRect screen_pos = getScreenRect(); + LLRect screen_pos = calcScreenRect(); LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_left), screen_pos.mBottom + llfloor(cursor_top) ); ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); diff --git a/indra/llui/lltrans.cpp b/indra/llui/lltrans.cpp index eee2ddcf2..f7988c965 100644 --- a/indra/llui/lltrans.cpp +++ b/indra/llui/lltrans.cpp @@ -72,7 +72,11 @@ bool LLTrans::parseStrings(const std::string& xml_filename, const std::setgetTextContents()); + std::string contents; + //value is used for strings with preceeding spaces. If not present, fall back to getTextContents() + if(!string->getAttributeString("value",contents)) + contents=string->getTextContents(); + LLTransTemplate xml_template(string_name, contents); sStringTemplates[xml_template.mName] = xml_template; std::set::const_iterator iter = default_args.find(xml_template.mName); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 49767a547..5ff57e85f 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -42,10 +42,10 @@ static LLRegisterWidget r("ui_ctrl"); // NOTE: the LLFocusableElement implementation has been moved to llfocusmgr.cpp, to mirror the header where the class is defined. LLUICtrl::LLUICtrl() : + mViewModel(LLViewModelPtr(new LLViewModel)), mCommitSignal(NULL), mValidateSignal(NULL), mCommitCallback(NULL), - mLostTopCallback(NULL), mValidateCallback(NULL), mCallbackUserData(NULL), mTentative(FALSE), @@ -63,8 +63,8 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect& rect, BOOL mouse_opaqu LLView( name, rect, mouse_opaque, reshape ), mCommitSignal(NULL), mValidateSignal(NULL), + mViewModel(LLViewModelPtr(new LLViewModel)), mCommitCallback( on_commit_callback), - mLostTopCallback( NULL ), mValidateCallback( NULL ), mCallbackUserData( callback_userdata ), mTentative( FALSE ), @@ -103,12 +103,33 @@ BOOL LLUICtrl::isCtrl() const return TRUE; } +//virtual +void LLUICtrl::setValue(const LLSD& value) +{ + mViewModel->setValue(value); +} + //virtual LLSD LLUICtrl::getValue() const { - return LLSD(); + return mViewModel->getValue(); } +/// When two widgets are displaying the same data (e.g. during a skin +/// change), share their ViewModel. +void LLUICtrl::shareViewModelFrom(const LLUICtrl& other) +{ + // Because mViewModel is an LLViewModelPtr, this assignment will quietly + // dispose of the previous LLViewModel -- unless it's already shared by + // somebody else. + mViewModel = other.mViewModel; +} + +//virtual +LLViewModel* LLUICtrl::getViewModel() const +{ + return mViewModel; +} // virtual BOOL LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text ) { @@ -167,58 +188,6 @@ void LLUICtrl::setFocus(BOOL b) } } -void LLUICtrl::onFocusReceived() -{ - // trigger callbacks - LLFocusableElement::onFocusReceived(); - - // find first view in hierarchy above new focus that is a LLUICtrl - LLView* viewp = getParent(); - LLUICtrl* last_focus = dynamic_cast(gFocusMgr.getLastKeyboardFocus()); - - while (viewp && !viewp->isCtrl()) - { - viewp = viewp->getParent(); - } - - // and if it has newly gained focus, call onFocusReceived() - LLUICtrl* ctrlp = static_cast(viewp); - if (ctrlp && (!last_focus || !last_focus->hasAncestor(ctrlp))) - { - ctrlp->onFocusReceived(); - } -} - -void LLUICtrl::onFocusLost() -{ - // trigger callbacks - LLFocusableElement::onFocusLost(); - - // find first view in hierarchy above old focus that is a LLUICtrl - LLView* viewp = getParent(); - while (viewp && !viewp->isCtrl()) - { - viewp = viewp->getParent(); - } - - // and if it has just lost focus, call onFocusReceived() - LLUICtrl* ctrlp = static_cast(viewp); - // hasFocus() includes any descendants - if (ctrlp && !ctrlp->hasFocus()) - { - ctrlp->onFocusLost(); - } -} - -void LLUICtrl::onLostTop() -{ - if (mLostTopCallback) - { - mLostTopCallback(this, mCallbackUserData); - } -} - - // virtual void LLUICtrl::setTabStop( BOOL b ) { @@ -240,12 +209,13 @@ BOOL LLUICtrl::acceptsTextInput() const //virtual BOOL LLUICtrl::isDirty() const { - return FALSE; + return mViewModel->isDirty(); }; //virtual void LLUICtrl::resetDirty() { + mViewModel->resetDirty(); } // virtual diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 519b45c0b..454e94159 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -38,11 +38,13 @@ #include "llrect.h" #include "llsd.h" +#include "llviewmodel.h" // *TODO move dependency to .cpp file class LLUICtrl : public LLView { public: + typedef boost::function commit_callback_t; typedef boost::signals2::signal commit_signal_t; typedef boost::signals2::signal enable_signal_t; @@ -56,15 +58,15 @@ public: U32 reshape=FOLLOWS_NONE); /*virtual*/ ~LLUICtrl(); + // We need this virtual so we can override it with derived versions + virtual LLViewModel* getViewModel() const; + // We shouldn't ever need to set this directly + //virtual void setViewModel(const LLViewModelPtr&); // LLView interface /*virtual*/ void initFromXML(LLXMLNodePtr node, LLView* parent); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - /*virtual*/ void onFocusReceived(); - /*virtual*/ void onFocusLost(); /*virtual*/ BOOL isCtrl() const; - /*virtual*/ void setTentative(BOOL b); - /*virtual*/ BOOL getTentative() const; // From LLFocusableElement /*virtual*/ void setFocus( BOOL b ); @@ -77,7 +79,14 @@ public: virtual class LLCtrlListInterface* getListInterface(); virtual class LLCtrlScrollInterface* getScrollInterface(); + virtual void setTentative(BOOL b); + virtual BOOL getTentative() const; + virtual void setValue(const LLSD& value); virtual LLSD getValue() const; + /// When two widgets are displaying the same data (e.g. during a skin + /// change), share their ViewModel. + virtual void shareViewModelFrom(const LLUICtrl& other); + virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); virtual void setIsChrome(BOOL is_chrome); @@ -89,7 +98,6 @@ public: virtual void resetDirty(); //Defaults to no-op // Call appropriate callbacks - virtual void onLostTop(); // called when registered as top ctrl and user clicks elsewhere virtual void onCommit(); // Default to no-op: @@ -125,7 +133,6 @@ public: void setCommitCallback( void (*cb)(LLUICtrl*, void*) ) { mCommitCallback = cb; } void setValidateBeforeCommit( BOOL(*cb)(LLUICtrl*, void*) ) { mValidateCallback = cb; } - void setLostTopCallback( void (*cb)(LLUICtrl*, void*) ) { mLostTopCallback = cb; } static LLView* fromXML(LLXMLNodePtr node, LLView* parent, class LLUICtrlFactory* factory); @@ -143,8 +150,9 @@ protected: commit_signal_t* mCommitSignal; enable_signal_t* mValidateSignal; + + LLViewModelPtr mViewModel; void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata ); - void (*mLostTopCallback)( LLUICtrl* ctrl, void* userdata ); BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); void* mCallbackUserData; diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index ef3a97be5..c51778917 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -38,6 +38,7 @@ #include "llcallbackmap.h" #include "llfloater.h" +#include "llinitparam.h" class LLView; class LLPanel; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 68616dc12..c476e25d3 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -79,74 +79,101 @@ LLView* LLView::sEditingUIView = NULL; S32 LLView::sLastLeftXML = S32_MIN; S32 LLView::sLastBottomXML = S32_MIN; +LLView::DrilldownFunc LLView::sDrilldown = + boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); + #if LL_DEBUG BOOL LLView::sIsDrawing = FALSE; #endif -LLView::LLView() : - mParentView(NULL), - mReshapeFlags(FOLLOWS_NONE), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(TRUE), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), - mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), - mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW), - // - mDelayedDelete(FALSE) - // +LLView::Follows::Follows() +: string(""), + flags("flags", FOLLOWS_NONE) +{} + +LLView::Params::Params() +: name("name", std::string("unnamed")), + enabled("enabled", true), + visible("visible", true), + mouse_opaque("mouse_opaque", true), + follows("follows"), + hover_cursor("hover_cursor", "UI_CURSOR_ARROW"), + use_bounding_rect("use_bounding_rect", false), + tab_group("tab_group", 0), + default_tab_group("default_tab_group"), + //tool_tip("tool_tip"), + sound_flags("sound_flags", MOUSE_UP), + layout("layout"), + rect("rect"), + bottom_delta("bottom_delta", S32_MAX), + top_pad("top_pad"), + top_delta("top_delta", S32_MAX), + left_pad("left_pad"), + left_delta("left_delta", S32_MAX), + from_xui("from_xui", true), + focus_root("focus_root", false), + needs_translate("translate"), + xmlns("xmlns"), + xmlns_xsi("xmlns:xsi"), + xsi_schemaLocation("xsi:schemaLocation"), + xsi_type("xsi:type") + { + addSynonym(rect, ""); } -LLView::LLView(const std::string& name, BOOL mouse_opaque) : - mParentView(NULL), - mName(name), - mReshapeFlags(FOLLOWS_NONE), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(mouse_opaque), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), - mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), - mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW), - // - mDelayedDelete(FALSE) - // +LLView::LLView(const LLView::Params& p) { + init(p); } - -LLView::LLView( - const std::string& name, const LLRect& rect, BOOL mouse_opaque, U32 reshape) : - mParentView(NULL), - mName(name), - mRect(rect), - mBoundingRect(rect), - mReshapeFlags(reshape), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(mouse_opaque), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), - mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), - mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW), - // - mDelayedDelete(FALSE) - // +void LLView::init(const LLView::Params& p) { + mVisible = p.visible; + mInDraw = false; + mName = p.name; + mParentView = NULL; + mReshapeFlags = FOLLOWS_NONE; + mSaveToXML = p.from_xui; + mIsFocusRoot = p.focus_root; + mLastVisible = FALSE; + mNextInsertionOrdinal = 0; + mHoverCursor = getCursorFromString(p.hover_cursor); + mEnabled = p.enabled; + mMouseOpaque = p.mouse_opaque; + mSoundFlags = p.sound_flags; + mUseBoundingRect = p.use_bounding_rect; + mDefaultTabGroup = p.default_tab_group; + mLastTabGroup = 0; + //mToolTipMsg((LLStringExplicit)p.tool_tip()), + //mDefaultWidgets(NULL) + + // create rect first, as this will supply initial follows flags + setShape(p.rect); + mReshapeFlags = p.follows.flags; +} + +LLView::LLView() +{ + init(LLView::Params()); +} + +LLView::LLView(const std::string& name, BOOL mouse_opaque) +{ + LLView::Params p; + p.name = name; + p.mouse_opaque = mouse_opaque; + init(p); +} + +LLView::LLView( const std::string& name, const LLRect& rect, BOOL mouse_opaque, U32 reshape) +{ + LLView::Params p; + p.name = name; + p.mouse_opaque = mouse_opaque; + p.rect = rect; + p.follows.flags = reshape; + init(p); } @@ -154,15 +181,10 @@ LLView::~LLView() { //llinfos << "Deleting view " << mName << ":" << (void*) this << llendl; // llassert(LLView::sIsDrawing == FALSE); - if( gFocusMgr.getKeyboardFocus() == this ) - { - llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl; - gFocusMgr.removeKeyboardFocusWithoutCallback( this ); - } if( hasMouseCapture() ) { - llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; + //llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; gFocusMgr.removeMouseCaptureWithoutCallback( this ); } @@ -236,7 +258,7 @@ void LLView::setUseBoundingRect( BOOL use_bounding_rect ) } } -BOOL LLView::getUseBoundingRect() +BOOL LLView::getUseBoundingRect() const { return mUseBoundingRect; } @@ -265,6 +287,7 @@ void LLView::sendChildToFront(LLView* child) void LLView::sendChildToBack(LLView* child) { +// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs if (child && child->getParent() == this) { // minor optimization, but more importantly, @@ -293,16 +316,18 @@ void LLView::moveChildToBackOfTabGroup(LLUICtrl* child) } } -void LLView::addChild(LLView* child, S32 tab_group) +// virtual +bool LLView::addChild(LLView* child, S32 tab_group) { if (!child) { - return; + return false; } if (mParentView == child) { llerrs << "Adding view " << child->getName() << " as child of itself" << llendl; } + // remove from current parent if (child->mParentView) { @@ -315,86 +340,57 @@ void LLView::addChild(LLView* child, S32 tab_group) // add to ctrl list if is LLUICtrl if (child->isCtrl()) { - // controls are stored in reverse order from render order - addCtrlAtEnd((LLUICtrl*) child, tab_group); + LLUICtrl* ctrl = static_cast(child); + mCtrlOrder.insert(tab_order_pair_t(ctrl, + tab_order_t(tab_group, mNextInsertionOrdinal))); + + mNextInsertionOrdinal++; } child->mParentView = this; updateBoundingRect(); + mLastTabGroup = tab_group; + return true; } -void LLView::addChildAtEnd(LLView* child, S32 tab_group) +bool LLView::addChildInBack(LLView* child, S32 tab_group) { - if (mParentView == child) + if(addChild(child, tab_group)) { - llerrs << "Adding view " << child->getName() << " as child of itself" << llendl; - } - // remove from current parent - if (child->mParentView) - { - child->mParentView->removeChild(child); + sendChildToBack(child); + return true; } - // add to back of child list - mChildList.push_back(child); - - // add to ctrl list if is LLUICtrl - if (child->isCtrl()) - { - // controls are stored in reverse order from render order - addCtrl((LLUICtrl*) child, tab_group); - } - - child->mParentView = this; - updateBoundingRect(); + return false; } // remove the specified child from the view, and set it's parent to NULL. -void LLView::removeChild(LLView* child, BOOL deleteIt) +void LLView::removeChild(LLView* child) { + //llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs if (child->mParentView == this) { + // if we are removing an item we are currently iterating over, that would be bad + llassert(child->mInDraw == false); mChildList.remove( child ); child->mParentView = NULL; if (child->isCtrl()) { - removeCtrl((LLUICtrl*)child); - } - if (deleteIt) - { - delete child; + child_tab_order_t::iterator found = mCtrlOrder.find(static_cast(child)); + if(found != mCtrlOrder.end()) + { + mCtrlOrder.erase(found); + } } } else { - llerrs << "LLView::removeChild called with non-child" << llendl; + llwarns << child->getName() << "is not a child of " << getName() << llendl; } updateBoundingRect(); } -void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group) -{ - mCtrlOrder.insert(tab_order_pair_t(ctrl, - tab_order_t(tab_group, mNextInsertionOrdinal++))); -} - -void LLView::addCtrl( LLUICtrl* ctrl, S32 tab_group) -{ - // add to front of list by using negative ordinal, which monotonically increases - mCtrlOrder.insert(tab_order_pair_t(ctrl, - tab_order_t(tab_group, -1 * mNextInsertionOrdinal++))); -} - -void LLView::removeCtrl(LLUICtrl* ctrl) -{ - child_tab_order_t::iterator found = mCtrlOrder.find(ctrl); - if(found != mCtrlOrder.end()) - { - mCtrlOrder.erase(found); - } -} - LLView::ctrl_list_t LLView::getCtrlList() const { ctrl_list_t controls; @@ -511,16 +507,6 @@ LLRect LLView::getRequiredRect() return mRect; } -//virtual -void LLView::onFocusLost() -{ -} - -//virtual -void LLView::onFocusReceived() -{ -} - BOOL LLView::focusNextRoot() { LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); @@ -636,11 +622,6 @@ void LLView::setVisible(BOOL visible) { if ( mVisible != visible ) { - if( !visible && (gFocusMgr.getTopCtrl() == this) ) - { - gFocusMgr.setTopCtrl( NULL ); - } - mVisible = visible; // notify children of visibility change if root, or part of visible hierarchy @@ -686,18 +667,10 @@ void LLView::setSnappedTo(const LLView* snap_view) BOOL LLView::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleHover( x, y, mask ) != NULL; - if( !handled - && blockMouseEvent(x, y) ) - { - LLUI::sWindow->setCursor(mHoverCursor); - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - handled = TRUE; - } - - return handled; + return childrenHandleHover( x, y, mask ) != NULL; } + std::string LLView::getShowNamesToolTip() { LLView* view = getParent(); @@ -726,7 +699,6 @@ std::string LLView::getShowNamesToolTip() return "/" + tool_tip; } - BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) { BOOL handled = FALSE; @@ -789,6 +761,128 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_s return handled; } +bool LLView::visibleAndContains(S32 local_x, S32 local_y) +{ + return sDrilldown(this, local_x, local_y) + && getVisible(); +} + +bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y) +{ + return visibleAndContains(local_x, local_y) + && getEnabled(); +} + +void LLView::logMouseEvent() +{ + if (sDebugMouseHandling) + { + sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage; + } +} + +template +LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method, + CHARTYPE c, MASK mask) +{ + if ( getVisible() && getEnabled() ) + { + BOOST_FOREACH(LLView* viewp, mChildList) + { + if ((viewp->*method)(c, mask, TRUE)) + { + if (LLView::sDebugKeys) + { + llinfos << desc << " handled by " << viewp->getName() << llendl; + } + return viewp; + } + } + } + return NULL; +} + +// XDATA might be MASK, or S32 clicks +template +LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block) +{ + BOOST_FOREACH(LLView* viewp, mChildList) + { + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + + if (!viewp->visibleEnabledAndContains(local_x, local_y)) + { + continue; + } + + if ((viewp->*method)( local_x, local_y, extra ) + || (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y ))) + { + viewp->logMouseEvent(); + return viewp; + } + } + return NULL; +} + +LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, + BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // default to not accepting drag and drop, will be overridden by handler + *accept = ACCEPT_NO; + + BOOST_FOREACH(LLView* viewp, mChildList) + { + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( !viewp->visibleEnabledAndContains(local_x, local_y)) + { + continue; + } + + // Differs from childrenHandleMouseEvent() simply in that this virtual + // method call diverges pretty radically from the usual (x, y, int). + if (viewp->handleDragAndDrop(local_x, local_y, mask, drop, + cargo_type, + cargo_data, + accept, + tooltip_msg) + || viewp->blockMouseEvent(local_x, local_y)) + { + return viewp; + } + } + return NULL; +} + +LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) +{ + BOOST_FOREACH(LLView* viewp, mChildList) + { + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if(!viewp->visibleEnabledAndContains(local_x, local_y)) + { + continue; + } + + // This call differentiates this method from childrenHandleMouseEvent(). + LLUI::sWindow->setCursor(viewp->getHoverCursor()); + + if (viewp->handleHover(local_x, local_y, mask) + || viewp->blockMouseEvent(local_x, local_y)) + { + viewp->logMouseEvent(); + return viewp; + } + } + return NULL; +} BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent) { BOOL handled = FALSE; @@ -869,53 +963,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* accept, std::string& tooltip_msg) { - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - *accept = ACCEPT_NO; - handled = TRUE; - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl; - } - - return handled; -} - -LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - LLView* handled_view = FALSE; - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - if( getVisible() ) -// if( getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDragAndDrop(local_x, local_y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg)) - { - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; } void LLView::onMouseCaptureLost() @@ -929,23 +977,12 @@ BOOL LLView::hasMouseCapture() BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) { LLView* handled_view = childrenHandleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } // HACK If we're editing UI, select the leaf view that ate the click. if (sEditingUI && handled_view) @@ -964,339 +1001,85 @@ BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) } } - return handled; + return handled_view != NULL; } BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handleMouseDown(x, y, mask); - handled = TRUE; - } - return handled; + return childrenHandleDoubleClick( x, y, mask ) != NULL; } BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; - if( getVisible() && getEnabled() ) - { - handled = childrenHandleScrollWheel( x, y, clicks ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - } - return handled; + return childrenHandleScrollWheel( x, y, clicks ) != NULL; } BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = childrenHandleMiddleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } - - return handled; + return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMiddleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; } - LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) { - LLView* handled_view = NULL; - if (getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) - && viewp->getVisible() - && viewp->getEnabled() - && viewp->handleScrollWheel( local_x, local_y, clicks )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - - handled_view = viewp; - break; - } - } - } - return handled_view; -} - -LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) -{ - LLView* handled_view = NULL; - if (getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if(viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleHover(local_x, local_y, mask) ) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false); } // Called during downward traversal LLView* LLView::childrenHandleKey(KEY key, MASK mask) { - LLView* handled_view = NULL; - - if ( getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - if (viewp->handleKey(key, mask, TRUE)) - { - if (LLView::sDebugKeys) - { - llinfos << "Key handled by " << viewp->getName() << llendl; - } - handled_view = viewp; - break; - } - } - } - - return handled_view; + return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask); } // Called during downward traversal LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char) { - LLView* handled_view = NULL; - - if ( getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - if (viewp->handleUnicodeChar(uni_char, TRUE)) - { - if (LLView::sDebugKeys) - { - llinfos << "Unicode character handled by " << viewp->getName() << llendl; - } - handled_view = viewp; - break; - } - } - } - - return handled_view; + return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask, + uni_char, MASK_NONE); } LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMouseDown( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask); } LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - - if (getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseDown( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask); } LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - - if (getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMiddleMouseDown( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask); } LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - - if (getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDoubleClick( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask); } LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - if( getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y)) - continue; - if (!viewp->getVisible()) - continue; - if (!viewp->getEnabled()) - continue; - if (viewp->handleMouseUp( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask); } LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) { - LLView* handled_view = NULL; - if( getVisible() && getEnabled() ) - { - BOOST_FOREACH(LLView* viewp, mChildList) - { - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseUp( local_x, local_y, mask )) - { - if (sDebugMouseHandling) - { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; - } - handled_view = viewp; - break; - } - } - } - return handled_view; + return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask); } LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) @@ -1327,56 +1110,72 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) void LLView::draw() { - if (sDebugRects) - { - drawDebugRect(); + drawChildren(); +} - // Check for bogus rectangle - if (getRect().mRight <= getRect().mLeft - || getRect().mTop <= getRect().mBottom) - { - llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; - } - } - - LLRect rootRect = getRootView()->getRect(); - LLRect screenRect; +void LLView::drawChildren() +{ // draw focused control on top of everything else - LLUICtrl* focus_view = dynamic_cast(gFocusMgr.getKeyboardFocus()); + /*LLUICtrl* focus_view = dynamic_cast(gFocusMgr.getKeyboardFocus()); if (focus_view && focus_view->getParent() != this) { focus_view = NULL; - } - - ++sDepth; - for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) + }*/ + if (!mChildList.empty()) { - LLView *viewp = *child_iter; + LLView* rootp = getRootView(); + ++sDepth; - if (viewp->getVisible() && viewp != focus_view && viewp->getRect().isValid()) + for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) { - // Only draw views that are within the root view - localRectToScreen(viewp->getRect(),&screenRect); - if ( rootRect.overlaps(screenRect) ) - { - gGL.matrixMode(LLRender::MM_MODELVIEW); - LLUI::pushMatrix(); + child_list_reverse_iter_t child = child_iter++; + LLView *viewp = *child; + + if (viewp == NULL) { - LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f); - viewp->draw(); + continue; + } + + + if (viewp->getVisible() && /*viewp != focus_view && */viewp->getRect().isValid()) + { + // Only draw views that are within the root view + LLRect screen_rect = viewp->calcScreenRect(); + if ( rootp->getLocalRect().overlaps(screen_rect) ) + { + //gGL.matrixMode(LLRender::MM_MODELVIEW); + LLUI::pushMatrix(); + { + LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f); + // flag the fact we are in draw here, in case overridden draw() method attempts to remove this widget + viewp->mInDraw = true; + viewp->draw(); + viewp->mInDraw = false; + + if (sDebugRects) + { + viewp->drawDebugRect(); + + // Check for bogus rectangle + if (!getRect().isValid()) + { + llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; + } + } + } + LLUI::popMatrix(); } - LLUI::popMatrix(); } + } - + --sDepth; } - --sDepth; - if (focus_view && focus_view->getVisible()) + /*if (focus_view && focus_view->getVisible()) { drawChild(focus_view); - } + }*/ // HACK if (sEditingUI && this == sEditingUIView) @@ -1393,12 +1192,12 @@ void LLView::drawDebugRect() // drawing solids requires texturing be disabled gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (mUseBoundingRect) + if (getUseBoundingRect()) { LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom, 0.f); } - LLRect debug_rect = mUseBoundingRect ? mBoundingRect : mRect; + LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect; // draw red rectangle for the border LLColor4 border_color(0.f, 0.f, 0.f, 1.f); @@ -1541,43 +1340,51 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) updateBoundingRect(); } -void LLView::updateBoundingRect() +LLRect LLView::calcBoundingRect() { - if (isDead()) return; - - if (mUseBoundingRect) - { - LLRect local_bounding_rect = LLRect::null; + LLRect local_bounding_rect = LLRect::null; BOOST_FOREACH(LLView* childp, mChildList) { // ignore invisible and "top" children when calculating bounding rect - // such as combobox popups - if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) - { - continue; - } - - LLRect child_bounding_rect = childp->getBoundingRect(); - - if (local_bounding_rect.isEmpty()) - { - // start out with bounding rect equal to first visible child's bounding rect - local_bounding_rect = child_bounding_rect; - } - else - { - // accumulate non-null children rectangles - if (child_bounding_rect.notEmpty()) - { - local_bounding_rect.unionWith(child_bounding_rect); - } - } + // such as combobox popups + if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) + { + continue; } - mBoundingRect = local_bounding_rect; - // translate into parent-relative coordinates - mBoundingRect.translate(mRect.mLeft, mRect.mBottom); + LLRect child_bounding_rect = childp->getBoundingRect(); + + if (local_bounding_rect.isEmpty()) + { + // start out with bounding rect equal to first visible child's bounding rect + local_bounding_rect = child_bounding_rect; + } + else + { + // accumulate non-null children rectangles + if (!child_bounding_rect.isEmpty()) + { + local_bounding_rect.unionWith(child_bounding_rect); + } + } + } + + // convert to parent-relative coordinates + local_bounding_rect.translate(mRect.mLeft, mRect.mBottom); + return local_bounding_rect; +} + + +void LLView::updateBoundingRect() +{ + if (isDead()) return; + + LLRect cur_rect = mBoundingRect; + + if (getUseBoundingRect()) + { + mBoundingRect = calcBoundingRect(); } else { @@ -1585,21 +1392,40 @@ void LLView::updateBoundingRect() } // give parent view a chance to resize, in case we just moved, for example - if (getParent() && getParent()->mUseBoundingRect) + if (getParent() && getParent()->getUseBoundingRect()) { getParent()->updateBoundingRect(); } + + /*if (mBoundingRect != cur_rect) + { + dirtyRect(); + }*/ + } -LLRect LLView::getScreenRect() const +LLRect LLView::calcScreenRect() const { - // *FIX: check for one-off error LLRect screen_rect; localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom); localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop); return screen_rect; } +LLRect LLView::calcScreenBoundingRect() const +{ + LLRect screen_rect; + // get bounding rect, if used + LLRect bounding_rect = getUseBoundingRect() ? mBoundingRect : mRect; + + // convert to local coordinates, as defined by mRect + bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); + + localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom); + localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop); + return screen_rect; +} + LLRect LLView::getLocalBoundingRect() const { LLRect local_bounding_rect = getBoundingRect(); @@ -1646,15 +1472,19 @@ BOOL LLView::hasAncestor(const LLView* parentp) const BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const { - LLView *child = getChildView(childname, TRUE, FALSE); - if (child) + LLView *focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + + while (focus != NULL) { - return gFocusMgr.childHasKeyboardFocus(child); - } - else - { - return FALSE; + if (focus->getName() == childname) + { + return TRUE; + } + + focus = focus->getParent(); } + + return FALSE; } //----------------------------------------------------------------------------- @@ -1707,14 +1537,14 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_ BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const { - return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT) ? mBoundingRect.pointInRect( x, y ) : mRect.pointInRect( x, y ); } BOOL LLView::pointInView(S32 x, S32 y, EHitTestType type) const { - return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT) ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) : mRect.localPointInRect( x, y ); } @@ -1789,6 +1619,7 @@ LLView* LLView::getRootView() return view; } + BOOL LLView::deleteViewByHandle(LLHandle handle) { LLView* viewp = handle.get(); @@ -1803,74 +1634,145 @@ BOOL LLView::deleteViewByHandle(LLHandle handle) return viewp != NULL; } +LLView* LLView::findPrevSibling(LLView* child) +{ + child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child); + if (prev_it != mChildList.end() && prev_it != mChildList.begin()) + { + return *(--prev_it); + } + return NULL; +} + +LLView* LLView::findNextSibling(LLView* child) +{ + child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child); + if (next_it != mChildList.end()) + { + next_it++; + } + + return (next_it != mChildList.end()) ? *next_it : NULL; +} + + +LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BOOL allow_partial_outside) +{ + LLCoordGL delta; + + if (allow_partial_outside) + { + const S32 KEEP_ONSCREEN_PIXELS = 16; + + if( input.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft ) + { + delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS); + } + else + if( input.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight ) + { + delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS); + } + + if( input.mTop > constraint.mTop ) + { + delta.mY = constraint.mTop - input.mTop; + } + else + if( input.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom ) + { + delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS); + } + } + else + { + if( input.mLeft < constraint.mLeft ) + { + delta.mX = constraint.mLeft - input.mLeft; + } + else + if( input.mRight > constraint.mRight ) + { + delta.mX = constraint.mRight - input.mRight; + // compensate for left edge possible going off screen + delta.mX += llmax( 0, input.getWidth() - constraint.getWidth() ); + } + + if( input.mTop > constraint.mTop ) + { + delta.mY = constraint.mTop - input.mTop; + } + else + if( input.mBottom < constraint.mBottom ) + { + delta.mY = constraint.mBottom - input.mBottom; + // compensate for top edge possible going off screen + delta.mY -= llmax( 0, input.getHeight() - constraint.getHeight() ); + } + } + + return delta; +} // Moves the view so that it is entirely inside of constraint. // If the view will not fit because it's too big, aligns with the top and left. // (Why top and left? That's where the drag bars are for floaters.) BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside ) { - S32 delta_x = 0; - S32 delta_y = 0; + LLCoordGL translation = getNeededTranslation(getRect(), constraint, allow_partial_outside); - if (allow_partial_outside) + if (translation.mX != 0 || translation.mY != 0) { - const S32 KEEP_ONSCREEN_PIXELS = 16; - - if( getRect().mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft ) - { - delta_x = constraint.mLeft - (getRect().mRight - KEEP_ONSCREEN_PIXELS); - } - else - if( getRect().mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight ) - { - delta_x = constraint.mRight - (getRect().mLeft + KEEP_ONSCREEN_PIXELS); - } - - if( getRect().mTop > constraint.mTop ) - { - delta_y = constraint.mTop - getRect().mTop; - } - else - if( getRect().mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom ) - { - delta_y = constraint.mBottom - (getRect().mTop - KEEP_ONSCREEN_PIXELS); - } - } - else - { - if( getRect().mLeft < constraint.mLeft ) - { - delta_x = constraint.mLeft - getRect().mLeft; - } - else - if( getRect().mRight > constraint.mRight ) - { - delta_x = constraint.mRight - getRect().mRight; - // compensate for left edge possible going off screen - delta_x += llmax( 0, getRect().getWidth() - constraint.getWidth() ); - } - - if( getRect().mTop > constraint.mTop ) - { - delta_y = constraint.mTop - getRect().mTop; - } - else - if( getRect().mBottom < constraint.mBottom ) - { - delta_y = constraint.mBottom - getRect().mBottom; - // compensate for top edge possible going off screen - delta_y -= llmax( 0, getRect().getHeight() - constraint.getHeight() ); - } - } - - if (delta_x != 0 || delta_y != 0) - { - translate(delta_x, delta_y); + translate(translation.mX, translation.mY); return TRUE; } return FALSE; } +// move this view into "inside" but not onto "exclude" +// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect +BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside ) +{ + LLCoordGL translation = getNeededTranslation(getRect(), inside, allow_partial_outside); + + if (translation.mX != 0 || translation.mY != 0) + { + // translate ourselves into constraint rect + translate(translation.mX, translation.mY); + + // do we overlap with exclusion area? + // keep moving in the same direction to the other side of the exclusion rect + if (exclude.overlaps(getRect())) + { + // moving right + if (translation.mX > 0) + { + translate(exclude.mRight - getRect().mLeft, 0); + } + // moving left + else if (translation.mX < 0) + { + translate(exclude.mLeft - getRect().mRight, 0); + } + + // moving up + if (translation.mY > 0) + { + translate(0, exclude.mTop - getRect().mBottom); + } + // moving down + else if (translation.mY < 0) + { + translate(0, exclude.mBottom - getRect().mTop); + } + } + + return TRUE; + } + return FALSE; +} + + void LLView::centerWithin(const LLRect& bounds) { S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2; @@ -2917,6 +2819,13 @@ LLSD LLView::getValue() const return LLSD(); } +S32 LLView::notifyParent(const LLSD& info) +{ + LLView* parent = getParent(); + if(parent) + return parent->notifyParent(info); + return 0; +} LLView* LLView::createWidget(LLXMLNodePtr xml_node) const { // forward requests to ui ctrl factory diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 51d75870c..721c82fbb 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -54,6 +54,7 @@ #include "stdenums.h" #include "lluistring.h" #include "llcursortypes.h" +#include "llinitparam.h" #include "llfocusmgr.h" const U32 FOLLOWS_NONE = 0x00; @@ -68,88 +69,6 @@ const BOOL NOT_MOUSE_OPAQUE = FALSE; const U32 GL_NAME_UI_RESERVED = 2; -/* -// virtual functions defined in LLView: - -virtual BOOL isCtrl() const; - LLUICtrl -virtual BOOL isPanel(); - LLPanel -virtual void setRect(const LLRect &rect); - LLLineEditor -virtual void addCtrl( LLUICtrl* ctrl, S32 tab_group); -virtual void addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group); -virtual void removeCtrl( LLUICtrl* ctrl); - LLPanel -virtual BOOL canFocusChildren() const { return TRUE; } - LLFolderView -virtual void deleteAllChildren(); - LLFolderView, LLPanelInventory -virtual void setTentative(BOOL b) {} - LLUICtrl, LLSliderCtrl, LLSpinCtrl -virtual BOOL getTentative() const { return FALSE; } - LLUICtrl, LLCheckBoxCtrl -virtual void setVisible(BOOL visible); - LLFloater, LLAlertDialog, LLMenuItemGL, LLModalDialog -virtual void setEnabled(BOOL enabled) { mEnabled = enabled; } - LLCheckBoxCtrl, LLComboBox, LLLineEditor, LLMenuGL, LLRadioGroup, etc -virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ) { return FALSE; } - LLUICtrl, LLButton, LLCheckBoxCtrl, LLLineEditor, LLMenuGL, LLSliderCtrl -virtual void handleVisibilityChange ( BOOL curVisibilityIn ); - LLMenuGL -virtual LLRect getSnapRect() const { return mRect; } *TODO: Make non virtual - LLFloater -virtual LLRect getRequiredRect() { return mRect; } - LLScrolllistCtrl -virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - LLUICtrl, et. al. -virtual void translate( S32 x, S32 y ); - LLMenuGL -virtual void handleReshape(const LLRect& new_rect, bool by_user = false); - LLFloater, LLScrollLIstVtrl -virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0); -virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0); - LLScrollListCtrl -virtual BOOL canSnapTo(const LLView* other_view) { return other_view != this && other_view->getVisible(); } - LLFloater -virtual void setSnappedTo(const LLView* snap_view) {} - LLFloater -virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - * -virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); - * -virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,EDragAndDropType cargo_type,void* cargo_data,EAcceptance* accept,std::string& tooltip_msg); - * -virtual void draw(); - * - - * -virtual LLXMLNodePtr getXML(bool save_children = true) const; - * -virtual void initFromXML(LLXMLNodePtr node, LLView* parent); - * -virtual void onFocusLost() {} - LLUICtrl, LLScrollListCtrl, LLMenuGL, LLLineEditor, LLComboBox -virtual void onFocusReceived() {} - LLUICtrl, LLTextEditor, LLScrollListVtrl, LLMenuGL, LLLineEditor -virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; - LLTabContainer, LLPanel, LLMenuGL -virtual void setControlName(const std::string& control, LLView *context); - LLSliderCtrl, LLCheckBoxCtrl -virtual std::string getControlName() const { return mControlName; } - LLSliderCtrl, LLCheckBoxCtrl -virtual bool handleEvent(LLPointer event, const LLSD& userdata); - LLMenuItem -virtual void setValue(const LLSD& value); - * - -protected: -virtual BOOL handleKeyHere(KEY key, MASK mask); - * -virtual BOOL handleUnicodeCharHere(llwchar uni_char); - * -*/ - class LLUICtrlFactory; // maps xml strings to widget classes @@ -209,9 +128,73 @@ public: } }; -class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElement +class LLView +: public LLMouseHandler, // handles mouse events + public LLFocusableElement, // handles keyboard events + public LLMortician // lazy deletion + //public LLHandleProvider // passes out weak references to self { +public: + struct Follows : public LLInitParam::ChoiceBlock + { + Alternative string; + Alternative flags; + Follows(); + }; + + struct Params : public LLInitParam::Block + { + Mandatory name; + + Optional enabled, + visible, + mouse_opaque, + use_bounding_rect, + from_xui, + focus_root; + + Optional tab_group, + default_tab_group; + Optional tool_tip; + + Optional sound_flags; + Optional follows; + Optional hover_cursor; + + Optional layout; + Optional rect; + + // Historical bottom-left layout used bottom_delta and left_delta + // for relative positioning. New layout "topleft" prefers specifying + // based on top edge. + Optional bottom_delta, // from last bottom to my bottom + top_pad, // from last bottom to my top + top_delta, // from last top to my top + left_pad, // from last right to my left + left_delta; // from last left to my left + + //FIXME: get parent context involved in parsing traversal + Ignored needs_translate, // cue for translation tools + xmlns, // xml namespace + xmlns_xsi, // xml namespace + xsi_schemaLocation, // xml schema + xsi_type; // xml schema type + + Params(); + }; + + void initFromParams(const LLView::Params&); + +protected: + LLView(const LLView::Params&); + //friend class LLUICtrlFactory; +private: + void init(const LLView::Params&); + +private: + // widgets in general are not copyable + LLView(const LLView& other) {}; public: #if LL_DEBUG static BOOL sIsDrawing; @@ -294,7 +277,9 @@ public: void setSoundFlags(U8 flags) { mSoundFlags = flags; } void setName(std::string name) { mName = name; } void setUseBoundingRect( BOOL use_bounding_rect ); - BOOL getUseBoundingRect(); + BOOL getUseBoundingRect() const; + + ECursorType getHoverCursor() { return mHoverCursor; } const std::string& getToolTip() const { return mToolTipMsg.getString(); } @@ -302,15 +287,14 @@ public: void sendChildToBack(LLView* child); void moveChildToFrontOfTabGroup(LLUICtrl* child); void moveChildToBackOfTabGroup(LLUICtrl* child); + + virtual bool addChild(LLView* view, S32 tab_group = 0); + + // implemented in terms of addChild() + bool addChildInBack(LLView* view, S32 tab_group = 0); - void addChild(LLView* view, S32 tab_group = 0); - void addChildAtEnd(LLView* view, S32 tab_group = 0); // remove the specified child from the view, and set it's parent to NULL. - void removeChild(LLView* view, BOOL deleteIt = FALSE); - - virtual void addCtrl( LLUICtrl* ctrl, S32 tab_group); - virtual void addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group); - virtual void removeCtrl( LLUICtrl* ctrl); + virtual void removeChild(LLView* view); child_tab_order_t getCtrlOrder() const { return mCtrlOrder; } ctrl_list_t getCtrlList() const; @@ -318,6 +302,7 @@ public: void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; } S32 getDefaultTabGroup() const { return mDefaultTabGroup; } + S32 getLastTabGroup() { return mLastTabGroup; } BOOL isInVisibleChain() const; BOOL isInEnabledChain() const; @@ -336,18 +321,19 @@ public: void setAllChildrenEnabled(BOOL b); virtual void setVisible(BOOL visible); - BOOL getVisible() const { return mVisible; } + const BOOL& getVisible() const { return mVisible; } virtual void setEnabled(BOOL enabled); BOOL getEnabled() const { return mEnabled; } U8 getSoundFlags() const { return mSoundFlags; } virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - virtual void handleVisibilityChange ( BOOL curVisibilityIn ); + virtual void handleVisibilityChange ( BOOL new_visibility ); void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); } - void popVisible() { setVisible(mLastVisible); mLastVisible = TRUE; } - + void popVisible() { setVisible(mLastVisible); } + BOOL getLastVisible() const { return mLastVisible; } + LLHandle getHandle() { mHandle.bind(this); return mHandle; } U32 getFollows() const { return mReshapeFlags; } @@ -360,18 +346,22 @@ public: const LLRect& getRect() const { return mRect; } const LLRect& getBoundingRect() const { return mBoundingRect; } LLRect getLocalBoundingRect() const; - LLRect getScreenRect() const; + LLRect calcScreenRect() const; + LLRect calcScreenBoundingRect() const; LLRect getLocalRect() const; virtual LLRect getSnapRect() const; LLRect getLocalSnapRect() const; // Override and return required size for this object. 0 for width/height means don't care. virtual LLRect getRequiredRect(); + LLRect calcBoundingRect(); void updateBoundingRect(); LLView* getRootView(); LLView* getParent() const { return mParentView; } LLView* getFirstChild() const { return (mChildList.empty()) ? NULL : *(mChildList.begin()); } + LLView* findPrevSibling(LLView* child); + LLView* findNextSibling(LLView* child); S32 getChildCount() const { return (S32)mChildList.size(); } template void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); } BOOL hasAncestor(const LLView* parentp) const; @@ -388,6 +378,7 @@ public: virtual void translate( S32 x, S32 y ); void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); } BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside ); + BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside ); void centerWithin(const LLRect& bounds); void setShape(const LLRect& new_rect, bool by_user = false); @@ -422,10 +413,6 @@ public: BOOL getSaveToXML() const { return mSaveToXML; } void setSaveToXML(BOOL b) { mSaveToXML = b; } - // inherited from LLFocusableElement - /* virtual */ void onFocusLost(); - /* virtual */ void onFocusReceived(); - typedef enum e_hit_test_type { HIT_TEST_USE_BOUNDING_RECT, @@ -465,6 +452,8 @@ public: virtual LLSD getValue() const; const child_list_t* getChildList() const { return &mChildList; } + child_list_const_iter_t beginChild() const { return mChildList.begin(); } + child_list_const_iter_t endChild() const { return mChildList.end(); } // LLMouseHandler functions // Default behavior is to pass events to children @@ -592,12 +581,20 @@ public: static LLWindow* getWindow(void) { return LLUI::sWindow; } virtual void handleReshape(const LLRect& rect, bool by_user); -protected: + virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleUnicodeCharHere(llwchar uni_char); + + //send custom notification to LLView parent + virtual S32 notifyParent(const LLSD& info); +protected: void drawDebugRect(); void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE); + void drawChildren(); + bool visibleAndContains(S32 local_x, S32 local_Y); + bool visibleEnabledAndContains(S32 local_x, S32 local_y); + void logMouseEvent(); LLView* childrenHandleKey(KEY key, MASK mask); LLView* childrenHandleUnicodeChar(llwchar uni_char); @@ -624,18 +621,36 @@ protected: control_map_t mFloaterControls; private: + + template + LLView* childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block = true); + + template + LLView* childrenHandleCharEvent(const std::string& desc, const METHOD& method, + CHARTYPE c, MASK mask); + + // adapter to blur distinction between handleKey() and handleUnicodeChar() + // for childrenHandleCharEvent() + BOOL handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, BOOL from_parent) + { + return handleUnicodeChar(uni_char, from_parent); + } + LLView* mParentView; child_list_t mChildList; - std::string mName; // location in pixels, relative to surrounding structure, bottom,left=0,0 + BOOL mVisible; LLRect mRect; LLRect mBoundingRect; + + std::string mName; U32 mReshapeFlags; child_tab_order_t mCtrlOrder; S32 mDefaultTabGroup; + S32 mLastTabGroup; BOOL mEnabled; // Enabled means "accepts input that has an effect on the state of the application." // A disabled view, for example, may still have a scrollbar that responds to mouse events. @@ -651,14 +666,9 @@ private: LLRootHandle mHandle; BOOL mLastVisible; - BOOL mVisible; - S32 mNextInsertionOrdinal; - // -public: - BOOL mDelayedDelete; - // + bool mInDraw; private: static LLWindow* sWindow; // All root views must know about their window. @@ -674,6 +684,34 @@ private: boost::signals2::connection mControlConnection; ECursorType mHoverCursor; + + // This allows special mouse-event targeting logic for testing. + typedef boost::function DrilldownFunc; + static DrilldownFunc sDrilldown; +public: + // This is the only public accessor to alter sDrilldown. This is not + // an accident. The intended usage pattern is like: + // { + // LLView::TemporaryDrilldownFunc scoped_func(myfunctor); + // // ... test with myfunctor ... + // } // exiting block restores original LLView::sDrilldown + class TemporaryDrilldownFunc: public boost::noncopyable + { + public: + TemporaryDrilldownFunc(const DrilldownFunc& func): + mOldDrilldown(sDrilldown) + { + sDrilldown = func; + } + + ~TemporaryDrilldownFunc() + { + sDrilldown = mOldDrilldown; + } + + private: + DrilldownFunc mOldDrilldown; + }; public: static BOOL sDebugRects; // Draw debug rects behind everything. diff --git a/indra/llui/llviewmodel.cpp b/indra/llui/llviewmodel.cpp new file mode 100644 index 000000000..a9f8acc44 --- /dev/null +++ b/indra/llui/llviewmodel.cpp @@ -0,0 +1,157 @@ +/** + * @file llviewmodel.cpp + * @author Nat Goodspeed + * @date 2008-08-08 + * @brief Implementation for llviewmodel. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llviewmodel.h" +// STL headers +// std headers +// external library headers +// other Linden headers + +/// +LLViewModel::LLViewModel() + : mDirty(false) +{ +} + +/// Instantiate an LLViewModel with an existing data value +LLViewModel::LLViewModel(const LLSD& value) + : mDirty(false) +{ + setValue(value); +} + +/// Update the stored value +void LLViewModel::setValue(const LLSD& value) +{ + mValue = value; + mDirty = true; +} + +LLSD LLViewModel::getValue() const +{ + return mValue; +} + +//////////////////////////////////////////////////////////////////////////// + +/// +LLTextViewModel::LLTextViewModel() + : LLViewModel(false), + mUpdateFromDisplay(false) +{ +} + +/// Instantiate an LLViewModel with an existing data value +LLTextViewModel::LLTextViewModel(const LLSD& value) + : LLViewModel(value), + mUpdateFromDisplay(false) +{ +} + +/// Update the stored value +void LLTextViewModel::setValue(const LLSD& value) +{ + LLViewModel::setValue(value); + mDisplay = utf8str_to_wstring(value.asString()); + // mDisplay and mValue agree + mUpdateFromDisplay = false; +} + +void LLTextViewModel::setDisplay(const LLWString& value) +{ + // This is the strange way to alter the value. Normally we'd setValue() + // and do the utf8str_to_wstring() to get the corresponding mDisplay + // value. But a text editor might want to edit the display string + // directly, then convert back to UTF8 on commit. + mDisplay = value; + mDirty = true; + // Don't immediately convert to UTF8 -- do it lazily -- we expect many + // more setDisplay() calls than getValue() calls. Just flag that it needs + // doing. + mUpdateFromDisplay = true; +} + +LLSD LLTextViewModel::getValue() const +{ + // Has anyone called setDisplay() since the last setValue()? If so, have + // to convert mDisplay back to UTF8. + if (mUpdateFromDisplay) + { + // The fact that we're lazily updating fields in this object should be + // transparent to clients, which is why this method is left + // conventionally const. Nor do we particularly want to make these + // members mutable. Just cast away constness in this one place. + LLTextViewModel* nthis = const_cast(this); + nthis->mUpdateFromDisplay = false; + nthis->mValue = wstring_to_utf8str(mDisplay); + } + return LLViewModel::getValue(); +} + + +//////////////////////////////////////////////////////////////////////////// + +LLListViewModel::LLListViewModel(const LLSD& values) + : LLViewModel() +{ +} + +void LLListViewModel::addColumn(const LLSD& column, EAddPosition pos) +{ +} + +void LLListViewModel::clearColumns() +{ +} + +void LLListViewModel::setColumnLabel(const std::string& column, const std::string& label) +{ +} + +LLScrollListItem* LLListViewModel::addElement(const LLSD& value, EAddPosition pos, + void* userdata) +{ + return NULL; +} + +LLScrollListItem* LLListViewModel::addSimpleElement(const std::string& value, EAddPosition pos, + const LLSD& id) +{ + return NULL; +} + +void LLListViewModel::clearRows() +{ +} + +void LLListViewModel::sortByColumn(const std::string& name, bool ascending) +{ +} diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h new file mode 100644 index 000000000..763af5d8a --- /dev/null +++ b/indra/llui/llviewmodel.h @@ -0,0 +1,214 @@ +/** + * @file llviewmodel.h + * @author Nat Goodspeed + * @date 2008-08-08 + * @brief Define "View Model" classes intended to store data values for use + * by LLUICtrl subclasses. The phrase is borrowed from Microsoft + * terminology, in which "View Model" means the storage object + * underlying a specific widget object -- as in our case -- rather + * than the business "model" object underlying the overall "view" + * presented by the collection of widgets. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLVIEWMODEL_H) +#define LL_LLVIEWMODEL_H + +#include "llpointer.h" +#include "llsd.h" +#include "llrefcount.h" +#include "stdenums.h" +#include "llstring.h" +#include + +class LLScrollListItem; + +class LLViewModel; +class LLTextViewModel; +class LLListViewModel; +// Because LLViewModel is derived from LLRefCount, always pass, store +// and return LLViewModelPtr rather than plain LLViewModel*. +typedef LLPointer LLViewModelPtr; +typedef LLPointer LLTextViewModelPtr; +typedef LLPointer LLListViewModelPtr; + +/** + * LLViewModel stores a scalar LLSD data item, the current display value of a + * scalar LLUICtrl widget. LLViewModel subclasses are used to store data + * collections used for aggregate widgets. LLViewModel is ref-counted because + * -- for multiple skins -- we may have distinct widgets sharing the same + * LLViewModel data. This way, the LLViewModel is quietly deleted when the + * last referencing widget is destroyed. + */ +class LLViewModel: public LLRefCount +{ +public: + LLViewModel(); + /// Instantiate an LLViewModel with an existing data value + LLViewModel(const LLSD& value); + + /// Update the stored value + virtual void setValue(const LLSD& value); + /// Get the stored value, in appropriate type. + virtual LLSD getValue() const; + + /// Has the value been changed since last time we checked? + bool isDirty() const { return mDirty; } + /// Once the value has been saved to a file, or otherwise consumed by the + /// app, we no longer need to enable the Save button + void resetDirty() { mDirty = false; } + // + void setDirty() { mDirty = true; } + +protected: + LLSD mValue; + bool mDirty; +}; + +/** + * LLTextViewModel stores a value displayed as text. + */ +class LLTextViewModel: public LLViewModel +{ +public: + LLTextViewModel(); + /// Instantiate an LLViewModel with an existing data value + LLTextViewModel(const LLSD& value); + + // LLViewModel functions + virtual void setValue(const LLSD& value); + virtual LLSD getValue() const; + + // New functions + /// Get the stored value in string form + const LLWString& getDisplay() const { return mDisplay; } + + /** + * Set the display string directly (see LLTextEditor). What the user is + * editing is actually the LLWString value rather than the underlying + * UTF-8 value. + */ + void setDisplay(const LLWString& value); + +private: + /// To avoid converting every widget's stored value from LLSD to LLWString + /// every frame, cache the converted value + LLWString mDisplay; + /// As the user edits individual characters (setDisplay()), defer + /// LLWString-to-UTF8 conversions until s/he's done. + bool mUpdateFromDisplay; +}; + +/** + * LLListViewModel stores a list of data items. The semantics are borrowed + * from LLScrollListCtrl. + */ +class LLListViewModel: public LLViewModel +{ +public: + LLListViewModel() {} + LLListViewModel(const LLSD& values); + + virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM); + virtual void clearColumns(); + virtual void setColumnLabel(const std::string& column, const std::string& label); + virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, + void* userdata = NULL); + virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, + const LLSD& id); + virtual void clearRows(); + virtual void sortByColumn(const std::string& name, bool ascending); +}; + +//namespace LLViewModel +//{ +// class Value +// { +// public: +// Value(const LLSD& value = LLSD()); +// +// LLSD getValue() const { return mValue; } +// void setValue(const LLSD& value) { mValue = value; } +// +// bool isAvailable() const { return false; } +// bool isReadOnly() const { return false; } +// +// bool undo() { return false; } +// bool redo() { return false; } +// +// /// Has the value been changed since last time we checked? +// bool isDirty() const { return mDirty; } +// /// Once the value has been saved to a file, or otherwise consumed by the +// /// app, we no longer need to enable the Save button +// void resetDirty() { mDirty = false; } +// // +// void setDirty() { mDirty = true; } +// +// protected: +// LLSD mValue; +// bool mDirty; +// }; +// +// class Numeric : public Value +// { +// public: +// Numeric(S32 value = 0); +// Numeric(F32 value); +// +// F32 getPrecision(); +// F32 getMin(); +// F32 getMax(); +// +// void increment(); +// void decrement(); +// }; +// +// class MultipleValues : public Value +// { +// class Selector +// {}; +// +// MultipleValues(); +// virtual S32 numElements(); +// }; +// +// class Tuple : public MultipleValues +// { +// Tuple(S32 size); +// LLSD getValue(S32 which) const; +// void setValue(S32 which, const LLSD& value); +// }; +// +// class List : public MultipleValues +// { +// List(); +// +// void add(const ValueModel& value); +// bool remove(const Selector& item); +// +// void setSortElement(const Selector& element); +// void sort(); +// }; +// +//}; +#endif /* ! defined(LL_LLVIEWMODEL_H) */ diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 21afee3fb..99d052521 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -174,6 +174,8 @@ public: // Provide native key event data virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); } + virtual void setTitle(const std::string &title){}; + protected: LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags); virtual ~LLWindow(); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index ecc5efd4e..06d7f237e 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -3269,6 +3269,15 @@ void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async) } } +void LLWindowMacOSX::setTitle(const std::string &title) +{ + /*strncpy((char*)mWindowTitle + 1, title.c_str(), 253); + mWindowTitle[0] = title.length();*/ + + CFStringRef title_str = CFStringCreateWithCString(NULL, title.c_str(), kCFStringEncodingUTF8); + SetWindowTitleWithCFString(mWindow, title_str); +} + LLSD LLWindowMacOSX::getNativeKeyData() { LLSD result = LLSD::emptyMap(); diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 504b446d1..3096a0aec 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -118,6 +118,8 @@ public: /*virtual*/ void interruptLanguageTextInput(); /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + /*virtual*/ void setTitle(const std::string &title); + static std::vector getDynamicFallbackFontList(); // Provide native key event data diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index e7e6fe6b9..7458dbdc9 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -2512,6 +2512,11 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) llinfos << "spawn_web_browser returning." << llendl; } +void LLWindowSDL::setTitle(const std::string &title) +{ + mWindowTitle = title; + SDL_WM_SetCaption(mWindowTitle.c_str(),mWindowTitle.c_str()); +} void *LLWindowSDL::getPlatformWindow() { diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index a6b9ff4f9..4fbe89b5e 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -127,6 +127,8 @@ public: /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + /*virtual*/ void setTitle(const std::string &title); + static std::vector getDynamicFallbackFontList(); // Not great that these are public, but they have to be accessible diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index b047c123d..9b4c297ce 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3140,6 +3140,14 @@ void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async) ShellExecuteEx( &sei ); } +void LLWindowWin32::setTitle(const std::string &title) +{ + mbstowcs(mWindowTitle, title.c_str(), 255); + mWindowTitle[255] = 0; + + SetWindowText(mWindowHandle, mWindowTitle); +} + /* Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 9343593f6..fefe5d308 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -117,6 +117,8 @@ public: void ShellEx(const std::string& command); /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + /*virtual*/ void setTitle(const std::string &title); + LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); static std::vector getDynamicFallbackFontList(); diff --git a/indra/llxuixml/CMakeLists.txt b/indra/llxuixml/CMakeLists.txt new file mode 100644 index 000000000..bc98a43bd --- /dev/null +++ b/indra/llxuixml/CMakeLists.txt @@ -0,0 +1,40 @@ +# -*- cmake -*- + +project(llxuixml) + +include(00-Common) +include(LLCommon) +include(LLMath) +include(LLXML) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ) + +set(llxuixml_SOURCE_FILES + llinitparam.cpp + lluicolor.cpp + ) + +set(llxuixml_HEADER_FILES + CMakeLists.txt + + llinitparam.h + lluicolor.h + ) + +set_source_files_properties(${llxuixml_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES}) + +add_library (llxuixml ${llxuixml_SOURCE_FILES}) +# Libraries on which this library depends, needed for Linux builds +# Sort by high-level to low-level +target_link_libraries(llxuixml + llxml + llcommon + llmath + ) diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp new file mode 100644 index 000000000..db72aa19b --- /dev/null +++ b/indra/llxuixml/llinitparam.cpp @@ -0,0 +1,469 @@ +/** + * @file llinitparam.cpp + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llinitparam.h" + + +namespace LLInitParam +{ + // + // Param + // + Param::Param(BaseBlock* enclosing_block) + : mIsProvided(false) + { + const U8* my_addr = reinterpret_cast(this); + const U8* block_addr = reinterpret_cast(enclosing_block); + mEnclosingBlockOffset = 0x7FFFffff & (U32)(my_addr - block_addr); + } + + // + // ParamDescriptor + // + ParamDescriptor::ParamDescriptor(param_handle_t p, + merge_func_t merge_func, + deserialize_func_t deserialize_func, + serialize_func_t serialize_func, + validation_func_t validation_func, + inspect_func_t inspect_func, + S32 min_count, + S32 max_count) + : mParamHandle(p), + mMergeFunc(merge_func), + mDeserializeFunc(deserialize_func), + mSerializeFunc(serialize_func), + mValidationFunc(validation_func), + mInspectFunc(inspect_func), + mMinCount(min_count), + mMaxCount(max_count), + mUserData(NULL) + {} + + ParamDescriptor::ParamDescriptor() + : mParamHandle(0), + mMergeFunc(NULL), + mDeserializeFunc(NULL), + mSerializeFunc(NULL), + mValidationFunc(NULL), + mInspectFunc(NULL), + mMinCount(0), + mMaxCount(0), + mUserData(NULL) + {} + + ParamDescriptor::~ParamDescriptor() + { + delete mUserData; + } + + // + // Parser + // + Parser::~Parser() + {} + + void Parser::parserWarning(const std::string& message) + { + if (mParseSilently) return; + llwarns << message << llendl; + } + + void Parser::parserError(const std::string& message) + { + if (mParseSilently) return; + llerrs << message << llendl; + } + + + // + // BlockDescriptor + // + void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data) + { + mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end()); + std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams)); + std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList)); + std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams)); + } + + BlockDescriptor::BlockDescriptor() + : mMaxParamOffset(0), + mInitializationState(UNINITIALIZED), + mCurrentBlockPtr(NULL) + {} + + // called by each derived class in least to most derived order + void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) + { + descriptor.mCurrentBlockPtr = this; + descriptor.mMaxParamOffset = block_size; + + switch(descriptor.mInitializationState) + { + case BlockDescriptor::UNINITIALIZED: + // copy params from base class here + descriptor.aggregateBlockData(base_descriptor); + + descriptor.mInitializationState = BlockDescriptor::INITIALIZING; + break; + case BlockDescriptor::INITIALIZING: + descriptor.mInitializationState = BlockDescriptor::INITIALIZED; + break; + case BlockDescriptor::INITIALIZED: + // nothing to do + break; + } + } + + param_handle_t BaseBlock::getHandleFromParam(const Param* param) const + { + const U8* param_address = reinterpret_cast(param); + const U8* baseblock_address = reinterpret_cast(this); + return (param_address - baseblock_address); + } + + bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent) + { + if (!deserializeBlock(p, std::make_pair(name_stack.begin(), name_stack.end()), true)) + { + if (!silent) + { + p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str())); + } + return false; + } + return true; + } + + + bool BaseBlock::validateBlock(bool emit_errors) const + { + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it) + { + const Param* param = getParamFromHandle(it->first); + if (!it->second(param)) + { + if (emit_errors) + { + llwarns << "Invalid param \"" << getParamName(block_data, param) << "\"" << llendl; + } + return false; + } + } + return true; + } + + void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const + { + // named param is one like LLView::Params::follows + // unnamed param is like LLView::Params::rect - implicit + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + + for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); + it != block_data.mUnnamedParams.end(); + ++it) + { + param_handle_t param_handle = (*it)->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc; + if (serialize_func) + { + const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; + // each param descriptor remembers its serial number + // so we can inspect the same param under different names + // and see that it has the same number + name_stack.push_back(std::make_pair("", true)); + serialize_func(*param, parser, name_stack, diff_param); + name_stack.pop_back(); + } + } + + for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); + it != block_data.mNamedParams.end(); + ++it) + { + param_handle_t param_handle = it->second->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc; + if (serialize_func && param->anyProvided()) + { + // Ensure this param has not already been serialized + // Prevents from being serialized as its own tag. + bool duplicate = false; + for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin(); + it2 != block_data.mUnnamedParams.end(); + ++it2) + { + if (param_handle == (*it2)->mParamHandle) + { + duplicate = true; + break; + } + } + + //FIXME: for now, don't attempt to serialize values under synonyms, as current parsers + // don't know how to detect them + if (duplicate) + { + continue; + } + + name_stack.push_back(std::make_pair(it->first, !duplicate)); + const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; + serialize_func(*param, parser, name_stack, diff_param); + name_stack.pop_back(); + } + } + } + + bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const + { + // named param is one like LLView::Params::follows + // unnamed param is like LLView::Params::rect - implicit + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + + for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); + it != block_data.mUnnamedParams.end(); + ++it) + { + param_handle_t param_handle = (*it)->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::inspect_func_t inspect_func = (*it)->mInspectFunc; + if (inspect_func) + { + name_stack.push_back(std::make_pair("", true)); + inspect_func(*param, parser, name_stack, (*it)->mMinCount, (*it)->mMaxCount); + name_stack.pop_back(); + } + } + + for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); + it != block_data.mNamedParams.end(); + ++it) + { + param_handle_t param_handle = it->second->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc; + if (inspect_func) + { + // Ensure this param has not already been inspected + bool duplicate = false; + for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin(); + it2 != block_data.mUnnamedParams.end(); + ++it2) + { + if (param_handle == (*it2)->mParamHandle) + { + duplicate = true; + break; + } + } + + name_stack.push_back(std::make_pair(it->first, !duplicate)); + inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount); + name_stack.pop_back(); + } + } + + return true; + } + + bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool ignored) + { + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + bool names_left = name_stack_range.first != name_stack_range.second; + + bool new_name = names_left + ? name_stack_range.first->second + : true; + + if (names_left) + { + const std::string& top_name = name_stack_range.first->first; + + ParamDescriptor::deserialize_func_t deserialize_func = NULL; + Param* paramp = NULL; + + BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name); + if (found_it != block_data.mNamedParams.end()) + { + // find pointer to member parameter from offset table + paramp = getParamFromHandle(found_it->second->mParamHandle); + deserialize_func = found_it->second->mDeserializeFunc; + + Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second); + ++new_name_stack.first; + if (deserialize_func(*paramp, p, new_name_stack, new_name)) + { + // value is no longer new, we know about it now + name_stack_range.first->second = false; + return true; + } + else + { + return false; + } + } + } + + // try to parse unnamed parameters, in declaration order + for ( BlockDescriptor::param_list_t::iterator it = block_data.mUnnamedParams.begin(); + it != block_data.mUnnamedParams.end(); + ++it) + { + Param* paramp = getParamFromHandle((*it)->mParamHandle); + ParamDescriptor::deserialize_func_t deserialize_func = (*it)->mDeserializeFunc; + + if (deserialize_func && deserialize_func(*paramp, p, name_stack_range, new_name)) + { + return true; + } + } + + // if no match, and no names left on stack, this is just an existence assertion of this block + // verify by calling readValue with NoParamValue type, an inherently unparseable type + if (!names_left) + { + Flag no_value; + return p.readValue(no_value); + } + + return false; + } + + //static + void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name) + { + // create a copy of the param descriptor in mAllParams + // so other data structures can store a pointer to it + block_data.mAllParams.push_back(in_param); + ParamDescriptorPtr param(block_data.mAllParams.back()); + + std::string name(char_name); + if ((size_t)param->mParamHandle > block_data.mMaxParamOffset) + { + llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block" << llendl; + } + + if (name.empty()) + { + block_data.mUnnamedParams.push_back(param); + } + else + { + // don't use insert, since we want to overwrite existing entries + block_data.mNamedParams[name] = param; + } + + if (param->mValidationFunc) + { + block_data.mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); + } + } + + void BaseBlock::addSynonym(Param& param, const std::string& synonym) + { + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + if (block_data.mInitializationState == BlockDescriptor::INITIALIZING) + { + param_handle_t handle = getHandleFromParam(¶m); + + // check for invalid derivation from a paramblock (i.e. without using + // Block + if ((size_t)handle > block_data.mMaxParamOffset) + { + llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block" << llendl; + } + + ParamDescriptorPtr param_descriptor = findParamDescriptor(param); + if (param_descriptor) + { + if (synonym.empty()) + { + block_data.mUnnamedParams.push_back(param_descriptor); + } + else + { + block_data.mNamedParams[synonym] = param_descriptor; + } + } + } + } + + const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const + { + param_handle_t handle = getHandleFromParam(paramp); + for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it) + { + if (it->second->mParamHandle == handle) + { + return it->first; + } + } + + return LLStringUtil::null; + } + + ParamDescriptorPtr BaseBlock::findParamDescriptor(const Param& param) + { + param_handle_t handle = getHandleFromParam(¶m); + BlockDescriptor& descriptor = mostDerivedBlockDescriptor(); + BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end(); + for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin(); + it != end_it; + ++it) + { + if ((*it)->mParamHandle == handle) return *it; + } + return ParamDescriptorPtr(); + } + + // take all provided params from other and apply to self + // NOTE: this requires that "other" is of the same derived type as this + bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) + { + bool some_param_changed = false; + BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end(); + for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin(); + it != end_it; + ++it) + { + const Param* other_paramp = other.getParamFromHandle((*it)->mParamHandle); + ParamDescriptor::merge_func_t merge_func = (*it)->mMergeFunc; + if (merge_func) + { + Param* paramp = getParamFromHandle((*it)->mParamHandle); + llassert(paramp->mEnclosingBlockOffset == (*it)->mParamHandle); + some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); + } + } + return some_param_changed; + } +} diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h new file mode 100644 index 000000000..ab2095776 --- /dev/null +++ b/indra/llxuixml/llinitparam.h @@ -0,0 +1,2294 @@ +/** + * @file llinitparam.h + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPARAM_H +#define LL_LLPARAM_H + +#include +#include +#include +#include +#include + +#include "llerror.h" + +namespace LLInitParam +{ + // used to indicate no matching value to a given name when parsing + struct Flag{}; + + template const T& defaultValue() { static T value; return value; } + + template ::value > + struct ParamCompare + { + static bool equals(const T &a, const T &b) + { + return a == b; + } + }; + + // boost function types are not comparable + template + struct ParamCompare + { + static bool equals(const T&a, const T &b) + { + return false; + } + }; + + template<> + struct ParamCompare + { + static bool equals(const LLSD &a, const LLSD &b) { return false; } + }; + + template<> + struct ParamCompare + { + static bool equals(const Flag& a, const Flag& b) { return false; } + }; + + + // helper functions and classes + typedef ptrdiff_t param_handle_t; + + // empty default implementation of key cache + // leverages empty base class optimization + template + class TypeValues + { + private: + struct Inaccessable{}; + public: + typedef std::map value_name_map_t; + typedef Inaccessable name_t; + + void setValueName(const std::string& key) {} + std::string getValueName() const { return ""; } + std::string calcValueName(const T& value) const { return ""; } + void clearValueName() const {} + + static bool getValueFromName(const std::string& name, T& value) + { + return false; + } + + static bool valueNamesExist() + { + return false; + } + + static std::vector* getPossibleValues() + { + return NULL; + } + + static value_name_map_t* getValueNames() {return NULL;} + }; + + template > + class TypeValuesHelper + { + public: + typedef typename std::map value_name_map_t; + typedef std::string name_t; + + //TODO: cache key by index to save on param block size + void setValueName(const std::string& value_name) + { + mValueName = value_name; + } + + std::string getValueName() const + { + return mValueName; + } + + std::string calcValueName(const T& value) const + { + value_name_map_t* map = getValueNames(); + for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end(); + it != end_it; + ++it) + { + if (ParamCompare::equals(it->second, value)) + { + return it->first; + } + } + + return ""; + } + + void clearValueName() const + { + mValueName.clear(); + } + + static bool getValueFromName(const std::string& name, T& value) + { + value_name_map_t* map = getValueNames(); + typename value_name_map_t::iterator found_it = map->find(name); + if (found_it == map->end()) return false; + + value = found_it->second; + return true; + } + + static bool valueNamesExist() + { + return !getValueNames()->empty(); + } + + static value_name_map_t* getValueNames() + { + static value_name_map_t sMap; + static bool sInitialized = false; + + if (!sInitialized) + { + sInitialized = true; + DERIVED_TYPE::declareValues(); + } + return &sMap; + } + + static std::vector* getPossibleValues() + { + static std::vector sValues; + + value_name_map_t* map = getValueNames(); + for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end(); + it != end_it; + ++it) + { + sValues.push_back(it->first); + } + return &sValues; + } + + static void declare(const std::string& name, const T& value) + { + (*getValueNames())[name] = value; + } + + protected: + static void getName(const std::string& name, const T& value) + {} + + mutable std::string mValueName; + }; + + class Parser + { + LOG_CLASS(Parser); + + public: + + struct CompareTypeID + { + bool operator()(const std::type_info* lhs, const std::type_info* rhs) const + { + return lhs->before(*rhs); + } + }; + + typedef std::vector > name_stack_t; + typedef std::pair name_stack_range_t; + typedef std::vector possible_values_t; + + typedef bool (*parser_read_func_t)(Parser& parser, void* output); + typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&); + typedef boost::function parser_inspect_func_t; + + typedef std::map parser_read_func_map_t; + typedef std::map parser_write_func_map_t; + typedef std::map parser_inspect_func_map_t; + + Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map) + : mParseSilently(false), + mParserReadFuncs(&read_map), + mParserWriteFuncs(&write_map), + mParserInspectFuncs(&inspect_map) + {} + virtual ~Parser(); + + template bool readValue(T& param) + { + parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); + if (found_it != mParserReadFuncs->end()) + { + return found_it->second(*this, (void*)¶m); + } + return false; + } + + template bool writeValue(const T& param, name_stack_t& name_stack) + { + parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); + if (found_it != mParserWriteFuncs->end()) + { + return found_it->second(*this, (const void*)¶m, name_stack); + } + return false; + } + + // dispatch inspection to registered inspection functions, for each parameter in a param block + template bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) + { + parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T)); + if (found_it != mParserInspectFuncs->end()) + { + found_it->second(name_stack, min_count, max_count, possible_values); + return true; + } + return false; + } + + virtual std::string getCurrentElementName() = 0; + virtual void parserWarning(const std::string& message); + virtual void parserError(const std::string& message); + void setParseSilently(bool silent) { mParseSilently = silent; } + + protected: + template + void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL) + { + mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func)); + mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func)); + } + + template + void registerInspectFunc(parser_inspect_func_t inspect_func) + { + mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func)); + } + + bool mParseSilently; + + private: + parser_read_func_map_t* mParserReadFuncs; + parser_write_func_map_t* mParserWriteFuncs; + parser_inspect_func_map_t* mParserInspectFuncs; + }; + + class Param; + + // various callbacks and constraints associated with an individual param + struct ParamDescriptor + { + struct UserData + { + virtual ~UserData() {} + }; + + typedef bool(*merge_func_t)(Param&, const Param&, bool); + typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, bool); + typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param); + typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count); + typedef bool(*validation_func_t)(const Param*); + + ParamDescriptor(param_handle_t p, + merge_func_t merge_func, + deserialize_func_t deserialize_func, + serialize_func_t serialize_func, + validation_func_t validation_func, + inspect_func_t inspect_func, + S32 min_count, + S32 max_count); + + ParamDescriptor(); + ~ParamDescriptor(); + + param_handle_t mParamHandle; + merge_func_t mMergeFunc; + deserialize_func_t mDeserializeFunc; + serialize_func_t mSerializeFunc; + inspect_func_t mInspectFunc; + validation_func_t mValidationFunc; + S32 mMinCount; + S32 mMaxCount; + S32 mNumRefs; + UserData* mUserData; + }; + + typedef boost::shared_ptr ParamDescriptorPtr; + + // each derived Block class keeps a static data structure maintaining offsets to various params + class BlockDescriptor + { + public: + BlockDescriptor(); + + typedef enum e_initialization_state + { + UNINITIALIZED, + INITIALIZING, + INITIALIZED + } EInitializationState; + + void aggregateBlockData(BlockDescriptor& src_block_data); + + typedef boost::unordered_map param_map_t; + typedef std::vector param_list_t; + typedef std::list all_params_list_t; + typedef std::vector > param_validation_list_t; + + param_map_t mNamedParams; // parameters with associated names + param_list_t mUnnamedParams; // parameters with_out_ associated names + param_validation_list_t mValidationList; // parameters that must be validated + all_params_list_t mAllParams; // all parameters, owns descriptors + size_t mMaxParamOffset; + EInitializationState mInitializationState; // whether or not static block data has been initialized + class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed + }; + + class BaseBlock + { + public: + //TODO: implement in terms of owned_ptr + template + class Lazy + { + public: + Lazy() + : mPtr(NULL) + {} + + ~Lazy() + { + delete mPtr; + } + + Lazy(const Lazy& other) + { + if (other.mPtr) + { + mPtr = new T(*other.mPtr); + } + else + { + mPtr = NULL; + } + } + + Lazy& operator = (const Lazy& other) + { + if (other.mPtr) + { + mPtr = new T(*other.mPtr); + } + else + { + mPtr = NULL; + } + return *this; + } + + bool empty() const + { + return mPtr == NULL; + } + + void set(const T& other) + { + delete mPtr; + mPtr = new T(other); + } + + const T& get() const + { + return ensureInstance(); + } + + T& get() + { + return ensureInstance(); + } + + private: + // lazily allocate an instance of T + T* ensureInstance() const + { + if (mPtr == NULL) + { + mPtr = new T(); + } + return mPtr; + } + + private: + // if you get a compilation error with this, that means you are using a forward declared struct for T + // unfortunately, the type traits we rely on don't work with forward declared typed + //static const int dummy = sizeof(T); + + mutable T* mPtr; + }; + + // "Multiple" constraint types, put here in root class to avoid ambiguity during use + struct AnyAmount + { + enum { minCount = 0 }; + enum { maxCount = U32_MAX }; + }; + + template + struct AtLeast + { + enum { minCount = MIN_AMOUNT }; + enum { maxCount = U32_MAX }; + }; + + template + struct AtMost + { + enum { minCount = 0 }; + enum { maxCount = MAX_AMOUNT }; + }; + + template + struct Between + { + enum { minCount = MIN_AMOUNT }; + enum { maxCount = MAX_AMOUNT }; + }; + + template + struct Exactly + { + enum { minCount = EXACT_COUNT }; + enum { maxCount = EXACT_COUNT }; + }; + + // this typedef identifies derived classes as being blocks + typedef void baseblock_base_class_t; + LOG_CLASS(BaseBlock); + friend class Param; + + virtual ~BaseBlock() {} + bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false); + + param_handle_t getHandleFromParam(const Param* param) const; + bool validateBlock(bool emit_errors = true) const; + + Param* getParamFromHandle(const param_handle_t param_handle) + { + if (param_handle == 0) return NULL; + + U8* baseblock_address = reinterpret_cast(this); + return reinterpret_cast(baseblock_address + param_handle); + } + + const Param* getParamFromHandle(const param_handle_t param_handle) const + { + const U8* baseblock_address = reinterpret_cast(this); + return reinterpret_cast(baseblock_address + param_handle); + } + + void addSynonym(Param& param, const std::string& synonym); + + // Blocks can override this to do custom tracking of changes + virtual void paramChanged(const Param& changed_param, bool user_provided) {} + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name); + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const; + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + + // take all provided params from other and apply to self + bool overwriteFrom(const BaseBlock& other) + { + return false; + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const BaseBlock& other) + { + return false; + } + + static void addParam(BlockDescriptor& block_data, ParamDescriptorPtr param, const char* name); + + ParamDescriptorPtr findParamDescriptor(const Param& param); + + protected: + void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); + + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + return mergeBlock(block_data, source, overwrite); + } + // take all provided params from other and apply to self + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); + + static BlockDescriptor& selfBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + private: + const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; + }; + + template + struct ParamCompare, false > + { + static bool equals(const BaseBlock::Lazy& a, const BaseBlock::Lazy& b) { return !a.empty() || !b.empty(); } + }; + + class Param + { + public: + void setProvided(bool is_provided = true) + { + mIsProvided = is_provided; + enclosingBlock().paramChanged(*this, is_provided); + } + + Param& operator =(const Param& other) + { + mIsProvided = other.mIsProvided; + // don't change mEnclosingblockoffset + return *this; + } + protected: + + bool anyProvided() const { return mIsProvided; } + + Param(BaseBlock* enclosing_block); + + // store pointer to enclosing block as offset to reduce space and allow for quick copying + BaseBlock& enclosingBlock() const + { + const U8* my_addr = reinterpret_cast(this); + // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class + return *const_cast + (reinterpret_cast + (my_addr - (ptrdiff_t)(S32)mEnclosingBlockOffset)); + } + + private: + friend class BaseBlock; + + U32 mEnclosingBlockOffset:31; + U32 mIsProvided:1; + + }; + + // these templates allow us to distinguish between template parameters + // that derive from BaseBlock and those that don't + template + struct IsBlock + { + static const bool value = false; + struct EmptyBase {}; + typedef EmptyBase base_class_t; + }; + + template + struct IsBlock + { + static const bool value = true; + typedef BaseBlock base_class_t; + }; + + template + struct IsBlock, typename T::baseblock_base_class_t > + { + static const bool value = true; + typedef BaseBlock base_class_t; + }; + + template::value> + class ParamValue : public NAME_VALUE_LOOKUP + { + public: + typedef const T& value_assignment_t; + typedef T value_t; + typedef ParamValue self_t; + + ParamValue(): mValue() {} + ParamValue(value_assignment_t other) : mValue(other) {} + + void setValue(value_assignment_t val) + { + mValue = val; + } + + value_assignment_t getValue() const + { + return mValue; + } + + T& getValue() + { + return mValue; + } + + operator value_assignment_t() const + { + return mValue; + } + + value_assignment_t operator()() const + { + return mValue; + } + + void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) + { + *this = name; + } + + self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + { + if (NAME_VALUE_LOOKUP::getValueFromName(name, mValue)) + { + setValueName(name); + } + + return *this; + } + + protected: + T mValue; + }; + + template + class ParamValue + : public T, + public NAME_VALUE_LOOKUP + { + public: + typedef const T& value_assignment_t; + typedef T value_t; + typedef ParamValue self_t; + + ParamValue() + : T(), + mValidated(false) + {} + + ParamValue(value_assignment_t other) + : T(other), + mValidated(false) + {} + + void setValue(value_assignment_t val) + { + *this = val; + } + + value_assignment_t getValue() const + { + return *this; + } + + T& getValue() + { + return *this; + } + + operator value_assignment_t() const + { + return *this; + } + + value_assignment_t operator()() const + { + return *this; + } + + void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) + { + *this = name; + } + + self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + { + if (NAME_VALUE_LOOKUP::getValueFromName(name, *this)) + { + setValueName(name); + } + + return *this; + } + + protected: + mutable bool mValidated; // lazy validation flag + }; + + template + class ParamValue + : public NAME_VALUE_LOOKUP + { + public: + typedef const std::string& value_assignment_t; + typedef std::string value_t; + typedef ParamValue self_t; + + ParamValue(): mValue() {} + ParamValue(value_assignment_t other) : mValue(other) {} + + void setValue(value_assignment_t val) + { + if (NAME_VALUE_LOOKUP::getValueFromName(val, mValue)) + { + NAME_VALUE_LOOKUP::setValueName(val); + } + else + { + mValue = val; + } + } + + value_assignment_t getValue() const + { + return mValue; + } + + std::string& getValue() + { + return mValue; + } + + operator value_assignment_t() const + { + return mValue; + } + + value_assignment_t operator()() const + { + return mValue; + } + + protected: + std::string mValue; + }; + + + template > + struct ParamIterator + { + typedef typename std::vector >::const_iterator const_iterator; + typedef typename std::vector >::iterator iterator; + }; + + // specialize for custom parsing/decomposition of specific classes + // e.g. TypedParam has left, top, right, bottom, etc... + template, + bool HAS_MULTIPLE_VALUES = false, + bool VALUE_IS_BLOCK = IsBlock >::value> + class TypedParam + : public Param, + public ParamValue + { + public: + typedef TypedParam self_t; + typedef ParamValue param_value_t; + typedef typename param_value_t::value_assignment_t value_assignment_t; + typedef NAME_VALUE_LOOKUP name_value_lookup_t; + + using param_value_t::operator(); + + TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr) + { + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + BaseBlock::addParam(block_descriptor, param_descriptor, name); + } + + setValue(value); + } + + bool isProvided() const { return Param::anyProvided(); } + + static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast(param); + // no further names in stack, attempt to parse value now + if (name_stack_range.first == name_stack_range.second) + { + if (parser.readValue(typed_param.getValue())) + { + typed_param.clearValueName(); + typed_param.setProvided(); + return true; + } + + // try to parse a known named value + if(name_value_lookup_t::valueNamesExist()) + { + // try to parse a known named value + std::string name; + if (parser.readValue(name)) + { + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + + } + } + } + return false; + } + + static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) + { + const self_t& typed_param = static_cast(param); + if (!typed_param.isProvided()) return; + + if (!name_stack.empty()) + { + name_stack.back().second = true; + } + + std::string key = typed_param.getValueName(); + + // first try to write out name of name/value pair + + if (!key.empty()) + { + if (!diff_param || !ParamCompare::equals(static_cast(diff_param)->getValueName(), key)) + { + parser.writeValue(key, name_stack); + } + } + // then try to serialize value directly + else if (!diff_param || !ParamCompare::equals(typed_param.getValue(), static_cast(diff_param)->getValue())) + { + if (!parser.writeValue(typed_param.getValue(), name_stack)) + { + std::string calculated_key = typed_param.calcValueName(typed_param.getValue()); + if (!diff_param || !ParamCompare::equals(static_cast(diff_param)->getValueName(), calculated_key)) + { + parser.writeValue(calculated_key, name_stack); + } + } + } + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + // tell parser about our actual type + parser.inspectValue(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (name_value_lookup_t::getPossibleValues()) + { + parser.inspectValue(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + } + } + + void set(value_assignment_t val, bool flag_as_provided = true) + { + param_value_t::clearValueName(); + setValue(val); + setProvided(flag_as_provided); + } + + self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + { + return static_cast(param_value_t::operator =(name)); + } + + protected: + + self_t& operator =(const self_t& other) + { + param_value_t::operator =(other); + Param::operator =(other); + return *this; + } + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast(src); + self_t& dst_typed_param = static_cast(dst); + + if (src_typed_param.isProvided() + && (overwrite || !dst_typed_param.isProvided())) + { + dst_typed_param.set(src_typed_param.getValue()); + return true; + } + return false; + } + }; + + // parameter that is a block + template + class TypedParam + : public Param, + public ParamValue + { + public: + typedef ParamValue param_value_t; + typedef typename param_value_t::value_assignment_t value_assignment_t; + typedef TypedParam self_t; + typedef NAME_VALUE_LOOKUP name_value_lookup_t; + + using param_value_t::operator(); + + TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + param_value_t(value) + { + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + BaseBlock::addParam(block_descriptor, param_descriptor, name); + } + } + + static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast(param); + // attempt to parse block... + if(typed_param.deserializeBlock(parser, name_stack_range, new_name)) + { + typed_param.clearValueName(); + typed_param.setProvided(); + return true; + } + + if(name_value_lookup_t::valueNamesExist()) + { + // try to parse a known named value + std::string name; + if (parser.readValue(name)) + { + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + + } + } + return false; + } + + static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) + { + const self_t& typed_param = static_cast(param); + if (!typed_param.isProvided()) return; + + if (!name_stack.empty()) + { + name_stack.back().second = true; + } + + std::string key = typed_param.getValueName(); + if (!key.empty()) + { + if (!parser.writeValue(key, name_stack)) + { + return; + } + } + else + { + typed_param.serializeBlock(parser, name_stack, static_cast(diff_param)); + } + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + // I am a param that is also a block, so just recurse into my contents + const self_t& typed_param = static_cast(param); + typed_param.inspectBlock(parser, name_stack, min_count, max_count); + } + + // a param-that-is-a-block is provided when the user has set one of its child params + // *and* the block as a whole validates + bool isProvided() const + { + // only validate block when it hasn't already passed validation with current data + if (Param::anyProvided() && !param_value_t::mValidated) + { + // a sub-block is "provided" when it has been filled in enough to be valid + param_value_t::mValidated = param_value_t::validateBlock(false); + } + return Param::anyProvided() && param_value_t::mValidated; + } + + // assign block contents to this param-that-is-a-block + void set(value_assignment_t val, bool flag_as_provided = true) + { + setValue(val); + param_value_t::clearValueName(); + // force revalidation of block + // next call to isProvided() will update provision status based on validity + param_value_t::mValidated = false; + setProvided(flag_as_provided); + } + + self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + { + return static_cast(param_value_t::operator =(name)); + } + + // propagate changed status up to enclosing block + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + param_value_t::paramChanged(changed_param, user_provided); + if (user_provided) + { + // a child param has been explicitly changed + // so *some* aspect of this block is now provided + param_value_t::mValidated = false; + setProvided(); + param_value_t::clearValueName(); + } + else + { + Param::enclosingBlock().paramChanged(*this, user_provided); + } + } + + protected: + + self_t& operator =(const self_t& other) + { + param_value_t::operator =(other); + Param::operator =(other); + return *this; + } + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast(src); + self_t& dst_typed_param = static_cast(dst); + + if (src_typed_param.anyProvided()) + { + if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::selfBlockDescriptor(), src_typed_param, overwrite)) + { + dst_typed_param.clearValueName(); + dst_typed_param.setProvided(true); + return true; + } + } + return false; + } + }; + + // container of non-block parameters + template + class TypedParam + : public Param + { + public: + typedef TypedParam self_t; + typedef ParamValue param_value_t; + typedef typename std::vector container_t; + typedef const container_t& value_assignment_t; + + typedef typename param_value_t::value_t value_t; + typedef NAME_VALUE_LOOKUP name_value_lookup_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr) + { + std::copy(value.begin(), value.end(), std::back_inserter(mValues)); + + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + BaseBlock::addParam(block_descriptor, param_descriptor, name); + } + } + + bool isProvided() const { return Param::anyProvided(); } + + static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast(param); + value_t value; + // no further names in stack, attempt to parse value now + if (name_stack_range.first == name_stack_range.second) + { + // attempt to read value directly + if (parser.readValue(value)) + { + typed_param.add(value); + return true; + } + + // try to parse a known named value + if(name_value_lookup_t::valueNamesExist()) + { + // try to parse a known named value + std::string name; + if (parser.readValue(name)) + { + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, value)) + { + typed_param.add(value); + typed_param.mValues.back().setValueName(name); + return true; + } + + } + } + } + return false; + } + + static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) + { + const self_t& typed_param = static_cast(param); + if (!typed_param.isProvided() || name_stack.empty()) return; + + for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); + it != end_it; + ++it) + { + std::string key = it->getValueName(); + name_stack.back().second = true; + + if(key.empty()) + // not parsed via name values, write out value directly + { + bool value_written = parser.writeValue(*it, name_stack); + if (!value_written) + { + std::string calculated_key = it->calcValueName(it->getValue()); + if (!parser.writeValue(calculated_key, name_stack)) + { + break; + } + } + } + else + { + if(!parser.writeValue(key, name_stack)) + { + break; + } + } + } + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + parser.inspectValue(name_stack, min_count, max_count, NULL); + if (name_value_lookup_t::getPossibleValues()) + { + parser.inspectValue(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + } + } + + void set(value_assignment_t val, bool flag_as_provided = true) + { + mValues = val; + setProvided(flag_as_provided); + } + + param_value_t& add() + { + mValues.push_back(param_value_t(value_t())); + Param::setProvided(); + return mValues.back(); + } + + void add(const value_t& item) + { + param_value_t param_value; + param_value.setValue(item); + mValues.push_back(param_value); + setProvided(); + } + + void add(const typename name_value_lookup_t::name_t& name) + { + value_t value; + + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, value)) + { + add(value); + mValues.back().setValueName(name); + } + } + + // implicit conversion + operator value_assignment_t() const { return mValues; } + // explicit conversion + value_assignment_t operator()() const { return mValues; } + + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; + iterator begin() { return mValues.begin(); } + iterator end() { return mValues.end(); } + const_iterator begin() const { return mValues.begin(); } + const_iterator end() const { return mValues.end(); } + bool empty() const { return mValues.empty(); } + size_t size() const { return mValues.size(); } + + U32 numValidElements() const + { + return mValues.size(); + } + + protected: + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast(src); + self_t& dst_typed_param = static_cast(dst); + + if (overwrite) + { + std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); + } + else + { + container_t new_values(src_typed_param.mValues); + std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); + std::swap(dst_typed_param.mValues, new_values); + } + + if (src_typed_param.begin() != src_typed_param.end()) + { + dst_typed_param.setProvided(); + } + return true; + } + + container_t mValues; + }; + + // container of block parameters + template + class TypedParam + : public Param + { + public: + typedef TypedParam self_t; + typedef ParamValue param_value_t; + typedef typename std::vector container_t; + typedef const container_t& value_assignment_t; + typedef typename param_value_t::value_t value_t; + typedef NAME_VALUE_LOOKUP name_value_lookup_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr) + { + std::copy(value.begin(), value.end(), back_inserter(mValues)); + + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + BaseBlock::addParam(block_descriptor, param_descriptor, name); + } + } + + bool isProvided() const { return Param::anyProvided(); } + + static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast(param); + bool new_value = false; + + if (new_name || typed_param.mValues.empty()) + { + new_value = true; + typed_param.mValues.push_back(value_t()); + } + + param_value_t& value = typed_param.mValues.back(); + + // attempt to parse block... + if(value.deserializeBlock(parser, name_stack_range, new_name)) + { + typed_param.setProvided(); + return true; + } + else if(name_value_lookup_t::valueNamesExist()) + { + // try to parse a known named value + std::string name; + if (parser.readValue(name)) + { + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, value.getValue())) + { + typed_param.mValues.back().setValueName(name); + typed_param.setProvided(); + return true; + } + + } + } + + if (new_value) + { // failed to parse new value, pop it off + typed_param.mValues.pop_back(); + } + + return false; + } + + static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) + { + const self_t& typed_param = static_cast(param); + if (!typed_param.isProvided() || name_stack.empty()) return; + + for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); + it != end_it; + ++it) + { + name_stack.back().second = true; + + std::string key = it->getValueName(); + if (!key.empty()) + { + parser.writeValue(key, name_stack); + } + // Not parsed via named values, write out value directly + // NOTE: currently we don't worry about removing default values in Multiple + else + { + it->serializeBlock(parser, name_stack, NULL); + } + } + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + // I am a vector of blocks, so describe my contents recursively + param_value_t(value_t()).inspectBlock(parser, name_stack, min_count, max_count); + } + + void set(value_assignment_t val, bool flag_as_provided = true) + { + mValues = val; + setProvided(flag_as_provided); + } + + param_value_t& add() + { + mValues.push_back(value_t()); + setProvided(); + return mValues.back(); + } + + void add(const value_t& item) + { + mValues.push_back(item); + setProvided(); + } + + void add(const typename name_value_lookup_t::name_t& name) + { + value_t value; + + // try to parse a per type named value + if (name_value_lookup_t::getValueFromName(name, value)) + { + add(value); + mValues.back().setValueName(name); + } + } + + // implicit conversion + operator value_assignment_t() const { return mValues; } + // explicit conversion + value_assignment_t operator()() const { return mValues; } + + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; + iterator begin() { return mValues.begin(); } + iterator end() { return mValues.end(); } + const_iterator begin() const { return mValues.begin(); } + const_iterator end() const { return mValues.end(); } + bool empty() const { return mValues.empty(); } + size_t size() const { return mValues.size(); } + + U32 numValidElements() const + { + U32 count = 0; + for (const_iterator it = mValues.begin(), end_it = mValues.end(); + it != end_it; + ++it) + { + if(it->validateBlock(false)) count++; + } + return count; + } + + protected: + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast(src); + self_t& dst_typed_param = static_cast(dst); + + if (overwrite) + { + std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); + } + else + { + container_t new_values(src_typed_param.mValues); + std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); + std::swap(dst_typed_param.mValues, new_values); + } + + if (src_typed_param.begin() != src_typed_param.end()) + { + dst_typed_param.setProvided(); + } + + return true; + } + + container_t mValues; + }; + + template + class ChoiceBlock : public BASE_BLOCK + { + typedef ChoiceBlock self_t; + typedef ChoiceBlock enclosing_block_t; + typedef BASE_BLOCK base_block_t; + + LOG_CLASS(self_t); + public: + // take all provided params from other and apply to self + bool overwriteFrom(const self_t& other) + { + return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, true); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, false); + } + + bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + bool source_override = source_provided && (overwrite || !dest_provided); + + if (source_override || source.mCurChoice == mCurChoice) + { + return mergeBlock(block_data, source, overwrite); + } + return false; + } + + // merge with other block + bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite) + { + mCurChoice = other.mCurChoice; + return base_block_t::mergeBlock(selfBlockDescriptor(), other, overwrite); + } + + // clear out old choice when param has changed + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + param_handle_t changed_param_handle = base_block_t::getHandleFromParam(&changed_param); + // if we have a new choice... + if (changed_param_handle != mCurChoice) + { + // clear provided flag on previous choice + Param* previous_choice = base_block_t::getParamFromHandle(mCurChoice); + if (previous_choice) + { + previous_choice->setProvided(false); + } + mCurChoice = changed_param_handle; + } + base_block_t::paramChanged(changed_param, user_provided); + } + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + + protected: + ChoiceBlock() + : mCurChoice(0) + { + BaseBlock::init(selfBlockDescriptor(), base_block_t::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + } + + // Alternatives are mutually exclusive wrt other Alternatives in the same block. + // One alternative in a block will always have isChosen() == true. + // At most one alternative in a block will have isProvided() == true. + template > + class Alternative : public TypedParam + { + public: + friend class ChoiceBlock; + + typedef Alternative self_t; + typedef TypedParam >::value> super_t; + typedef typename super_t::value_assignment_t value_assignment_t; + + using super_t::operator =; + + explicit Alternative(const char* name = "", value_assignment_t val = defaultValue()) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1), + mOriginalValue(val) + { + // assign initial choice to first declared option + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr); + if (LL_UNLIKELY(DERIVED_BLOCK::selfBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) + { + if(blockp->mCurChoice == 0) + { + blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this); + } + } + } + + void choose() + { + static_cast(Param::enclosingBlock()).paramChanged(*this, true); + } + + void chooseAs(value_assignment_t val) + { + super_t::set(val); + } + + void operator =(value_assignment_t val) + { + super_t::set(val); + } + + void operator()(typename super_t::value_assignment_t val) + { + super_t::set(val); + } + + operator value_assignment_t() const + { + return (*this)(); + } + + value_assignment_t operator()() const + { + if (static_cast(Param::enclosingBlock()).getCurrentChoice() == this) + { + return super_t::getValue(); + } + return mOriginalValue; + } + + bool isChosen() const + { + return static_cast(Param::enclosingBlock()).getCurrentChoice() == this; + } + + private: + T mOriginalValue; + }; + + protected: + static BlockDescriptor& selfBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + private: + param_handle_t mCurChoice; + + const Param* getCurrentChoice() const + { + return base_block_t::getParamFromHandle(mCurChoice); + } + }; + + template + class Block + : public BASE_BLOCK + { + typedef Block self_t; + typedef Block block_t; + + public: + typedef BASE_BLOCK base_block_t; + + // take all provided params from other and apply to self + bool overwriteFrom(const self_t& other) + { + return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, true); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, false); + } + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + + protected: + Block() + { + //#pragma message("Parsing LLInitParam::Block") + BaseBlock::init(selfBlockDescriptor(), BASE_BLOCK::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + } + + // + // Nested classes for declaring parameters + // + template > + class Optional : public TypedParam + { + public: + typedef TypedParam >::value> super_t; + typedef typename super_t::value_assignment_t value_assignment_t; + + using super_t::operator(); + using super_t::operator =; + + explicit Optional(const char* name = "", value_assignment_t val = defaultValue()) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1) + { + //#pragma message("Parsing LLInitParam::Block::Optional") + } + + Optional& operator =(value_assignment_t val) + { + set(val); + return *this; + } + + DERIVED_BLOCK& operator()(value_assignment_t val) + { + super_t::set(val); + return static_cast(Param::enclosingBlock()); + } + }; + + template > + class Mandatory : public TypedParam + { + public: + typedef TypedParam >::value> super_t; + typedef Mandatory self_t; + typedef typename super_t::value_assignment_t value_assignment_t; + + using super_t::operator(); + using super_t::operator =; + + // mandatory parameters require a name to be parseable + explicit Mandatory(const char* name = "", value_assignment_t val = defaultValue()) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, 1, 1) + {} + + Mandatory& operator =(value_assignment_t val) + { + set(val); + return *this; + } + + DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + { + super_t::set(val); + return static_cast(Param::enclosingBlock()); + } + + static bool validate(const Param* p) + { + // valid only if provided + return static_cast(p)->isProvided(); + } + + }; + + template > + class Multiple : public TypedParam + { + public: + typedef TypedParam >::value> super_t; + typedef Multiple self_t; + typedef typename super_t::container_t container_t; + typedef typename super_t::value_assignment_t value_assignment_t; + typedef typename super_t::iterator iterator; + typedef typename super_t::const_iterator const_iterator; + + explicit Multiple(const char* name = "") + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) + {} + + Multiple& operator =(value_assignment_t val) + { + set(val); + return *this; + } + + DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + { + super_t::set(val); + return static_cast(Param::enclosingBlock()); + } + + static bool validate(const Param* paramp) + { + U32 num_valid = ((super_t*)paramp)->numValidElements(); + return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount; + } + }; + + class Deprecated : public Param + { + public: + explicit Deprecated(const char* name) + : Param(DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr) + { + BlockDescriptor& block_descriptor = DERIVED_BLOCK::selfBlockDescriptor(); + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + NULL, + &deserializeParam, + NULL, + NULL, + NULL, + 0, S32_MAX)); + BaseBlock::addParam(block_descriptor, param_descriptor, name); + } + } + + static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + { + if (name_stack_range.first == name_stack_range.second) + { + //std::string message = llformat("Deprecated value %s ignored", getName().c_str()); + //parser.parserWarning(message); + return true; + } + + return false; + } + }; + + // different semantics for documentation purposes, but functionally identical + typedef Deprecated Ignored; + + protected: + static BlockDescriptor& selfBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + template + void changeDefault(TypedParam& param, + typename TypedParam::value_assignment_t value) + { + if (!param.isProvided()) + { + param.set(value, false); + } + } + + }; + + template + class BatchBlock + : public Block + { + public: + typedef BatchBlock self_t; + typedef Block super_t; + + BatchBlock() + {} + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) + { + if (new_name) + { + // reset block + *static_cast(this) = defaultBatchValue(); + } + return super_t::deserializeBlock(p, name_stack_range, new_name); + } + + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) + { + if (overwrite) + { + *static_cast(this) = defaultBatchValue(); + // merge individual parameters into destination + return super_t::mergeBlock(super_t::selfBlockDescriptor(), other, overwrite); + } + return false; + } + protected: + static const DERIVED_BLOCK& defaultBatchValue() + { + static DERIVED_BLOCK default_value; + return default_value; + } + }; + + // FIXME: this specialization is not currently used, as it only matches against the BatchBlock base class + // and not the derived class with the actual params + template + class ParamValue , + NAME_VALUE_LOOKUP, + true> + : public NAME_VALUE_LOOKUP, + protected BatchBlock + { + public: + typedef BatchBlock block_t; + typedef const BatchBlock& value_assignment_t; + typedef block_t value_t; + + ParamValue() + : block_t(), + mValidated(false) + {} + + ParamValue(value_assignment_t other) + : block_t(other), + mValidated(false) + { + } + + void setValue(value_assignment_t val) + { + *this = val; + } + + value_assignment_t getValue() const + { + return *this; + } + + BatchBlock& getValue() + { + return *this; + } + + operator value_assignment_t() const + { + return *this; + } + + value_assignment_t operator()() const + { + return *this; + } + + protected: + mutable bool mValidated; // lazy validation flag + }; + + template + class ParamValue , + TypeValues, + IS_BLOCK> + : public IsBlock::base_class_t + { + public: + typedef ParamValue , TypeValues, false> self_t; + typedef const T& value_assignment_t; + typedef T value_t; + + ParamValue() + : mValue(), + mValidated(false) + {} + + ParamValue(value_assignment_t other) + : mValue(other), + mValidated(false) + {} + + void setValue(value_assignment_t val) + { + mValue.set(val); + } + + value_assignment_t getValue() const + { + return mValue.get(); + } + + T& getValue() + { + return mValue.get(); + } + + operator value_assignment_t() const + { + return mValue.get(); + } + + value_assignment_t operator()() const + { + return mValue.get(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) + { + return mValue.get().deserializeBlock(p, name_stack_range, new_name); + } + + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const + { + if (mValue.empty()) return; + + mValue.get().serializeBlock(p, name_stack, diff_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + if (mValue.empty()) return false; + + return mValue.get().inspectBlock(p, name_stack, min_count, max_count); + } + + protected: + mutable bool mValidated; // lazy validation flag + + private: + BaseBlock::Lazy mValue; + }; + + template <> + class ParamValue , + false> + : public TypeValues, + public BaseBlock + { + public: + typedef ParamValue, false> self_t; + typedef const LLSD& value_assignment_t; + + ParamValue() + : mValidated(false) + {} + + ParamValue(value_assignment_t other) + : mValue(other), + mValidated(false) + {} + + void setValue(value_assignment_t val) { mValue = val; } + + value_assignment_t getValue() const { return mValue; } + LLSD& getValue() { return mValue; } + + operator value_assignment_t() const { return mValue; } + value_assignment_t operator()() const { return mValue; } + + + // block param interface + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name); + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + //TODO: implement LLSD params as schema type Any + return true; + } + + protected: + mutable bool mValidated; // lazy validation flag + + private: + static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack); + + LLSD mValue; + }; + + template + class CustomParamValue + : public Block > >, + public TypeValues + { + public: + typedef enum e_value_age + { + VALUE_NEEDS_UPDATE, // mValue needs to be refreshed from the block parameters + VALUE_AUTHORITATIVE, // mValue holds the authoritative value (which has been replicated to the block parameters via updateBlockFromValue) + BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative + } EValueAge; + + typedef ParamValue > derived_t; + typedef CustomParamValue self_t; + typedef Block block_t; + typedef const T& value_assignment_t; + typedef T value_t; + + + CustomParamValue(const T& value = T()) + : mValue(value), + mValueAge(VALUE_AUTHORITATIVE), + mValidated(false) + {} + + bool deserializeBlock(Parser& parser, Parser::name_stack_range_t name_stack_range, bool new_name) + { + derived_t& typed_param = static_cast(*this); + // try to parse direct value T + if (name_stack_range.first == name_stack_range.second) + { + if(parser.readValue(typed_param.mValue)) + { + typed_param.mValueAge = VALUE_AUTHORITATIVE; + typed_param.updateBlockFromValue(false); + + typed_param.clearValueName(); + + return true; + } + } + + // fall back on parsing block components for T + return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name); + } + + void serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const + { + const derived_t& typed_param = static_cast(*this); + const derived_t* diff_param = static_cast(diff_block); + + std::string key = typed_param.getValueName(); + + // first try to write out name of name/value pair + if (!key.empty()) + { + if (!diff_param || !ParamCompare::equals(diff_param->getValueName(), key)) + { + parser.writeValue(key, name_stack); + } + } + // then try to serialize value directly + else if (!diff_param || !ParamCompare::equals(typed_param.getValue(), diff_param->getValue())) + { + + if (!parser.writeValue(typed_param.getValue(), name_stack)) + { + //RN: *always* serialize provided components of BlockValue (don't pass diff_param on), + // since these tend to be viewed as the constructor arguments for the value T. It seems + // cleaner to treat the uniqueness of a BlockValue according to the generated value, and + // not the individual components. This way will not + // be exported as , since it was probably the intent of the user to + // be specific about the RGB color values. This also fixes an issue where we distinguish + // between rect.left not being provided and rect.left being explicitly set to 0 (same as default) + + if (typed_param.mValueAge == VALUE_AUTHORITATIVE) + { + // if the value is authoritative but the parser doesn't accept the value type + // go ahead and make a copy, and splat the value out to its component params + // and serialize those params + derived_t copy(typed_param); + copy.updateBlockFromValue(true); + copy.block_t::serializeBlock(parser, name_stack, NULL); + } + else + { + block_t::serializeBlock(parser, name_stack, NULL); + } + } + } + } + + bool inspectBlock(Parser& parser, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + // first, inspect with actual type... + parser.inspectValue(name_stack, min_count, max_count, NULL); + if (TypeValues::getPossibleValues()) + { + //...then inspect with possible string values... + parser.inspectValue(name_stack, min_count, max_count, TypeValues::getPossibleValues()); + } + // then recursively inspect contents... + return block_t::inspectBlock(parser, name_stack, min_count, max_count); + } + + bool validateBlock(bool emit_errors = true) const + { + if (mValueAge == VALUE_NEEDS_UPDATE) + { + if (block_t::validateBlock(emit_errors)) + { + // clear stale keyword associated with old value + TypeValues::clearValueName(); + mValueAge = BLOCK_AUTHORITATIVE; + static_cast(const_cast(this))->updateValueFromBlock(); + return true; + } + else + { + //block value incomplete, so not considered provided + // will attempt to revalidate on next call to isProvided() + return false; + } + } + else + { + // we have a valid value in hand + return true; + } + } + + // propagate change status up to enclosing block + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + BaseBlock::paramChanged(changed_param, user_provided); + if (user_provided) + { + // a parameter changed, so our value is out of date + mValueAge = VALUE_NEEDS_UPDATE; + } + } + + void setValue(value_assignment_t val) + { + derived_t& typed_param = static_cast(*this); + // set param version number to be up to date, so we ignore block contents + mValueAge = VALUE_AUTHORITATIVE; + mValue = val; + typed_param.clearValueName(); + static_cast(this)->updateBlockFromValue(false); + } + + value_assignment_t getValue() const + { + validateBlock(true); + return mValue; + } + + T& getValue() + { + validateBlock(true); + return mValue; + } + + operator value_assignment_t() const + { + return getValue(); + } + + value_assignment_t operator()() const + { + return getValue(); + } + + protected: + + // use this from within updateValueFromBlock() to set the value without making it authoritative + void updateValue(value_assignment_t value) + { + mValue = value; + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + bool source_override = source_provided && (overwrite || !dst_provided); + + const derived_t& src_typed_param = static_cast(source); + + if (source_override && src_typed_param.mValueAge == VALUE_AUTHORITATIVE) + { + // copy value over + setValue(src_typed_param.getValue()); + return true; + } + // merge individual parameters into destination + if (mValueAge == VALUE_AUTHORITATIVE) + { + static_cast(this)->updateBlockFromValue(dst_provided); + } + return mergeBlock(block_data, source, overwrite); + } + + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + return block_t::mergeBlock(block_data, source, overwrite); + } + + mutable bool mValidated; // lazy validation flag + + private: + mutable T mValue; + mutable EValueAge mValueAge; + }; +} + + +#endif // LL_LLPARAM_H diff --git a/indra/llxuixml/lluicolor.cpp b/indra/llxuixml/lluicolor.cpp new file mode 100644 index 000000000..f9bb80f8c --- /dev/null +++ b/indra/llxuixml/lluicolor.cpp @@ -0,0 +1,87 @@ +/** + * @file lluicolor.cpp + * @brief brief LLUIColor class implementation file + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lluicolor.h" + +LLUIColor::LLUIColor() + :mColorPtr(NULL) +{ +} + + +LLUIColor::LLUIColor(const LLColor4& color) +: mColor(color), + mColorPtr(NULL) +{ +} + +LLUIColor::LLUIColor(const LLUIColor* color) +: mColorPtr(color) +{ +} + +void LLUIColor::set(const LLColor4& color) +{ + mColor = color; + mColorPtr = NULL; +} + +void LLUIColor::set(const LLUIColor* color) +{ + mColorPtr = color; +} + +const LLColor4& LLUIColor::get() const +{ + return (mColorPtr == NULL ? mColor : mColorPtr->get()); +} + +LLUIColor::operator const LLColor4& () const +{ + return get(); +} + +const LLColor4& LLUIColor::operator()() const +{ + return get(); +} + +bool LLUIColor::isReference() const +{ + return mColorPtr != NULL; +} + +namespace LLInitParam +{ + // used to detect equivalence with default values on export + bool ParamCompare::equals(const LLUIColor &a, const LLUIColor &b) + { + // do not detect value equivalence, treat pointers to colors as distinct from color values + return (a.mColorPtr == NULL && b.mColorPtr == NULL ? a.mColor == b.mColor : a.mColorPtr == b.mColorPtr); + } +} diff --git a/indra/llxuixml/lluicolor.h b/indra/llxuixml/lluicolor.h new file mode 100644 index 000000000..97ebea854 --- /dev/null +++ b/indra/llxuixml/lluicolor.h @@ -0,0 +1,71 @@ +/** + * @file lluicolor.h + * @brief brief LLUIColor class header file + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLUICOLOR_H_ +#define LL_LLUICOLOR_H_ + +#include "v4color.h" + +namespace LLInitParam +{ + template + struct ParamCompare; +} + +class LLUIColor +{ +public: + LLUIColor(); + LLUIColor(const LLColor4& color); + LLUIColor(const LLUIColor* color); + + void set(const LLColor4& color); + void set(const LLUIColor* color); + + const LLColor4& get() const; + + operator const LLColor4& () const; + const LLColor4& operator()() const; + + bool isReference() const; + +private: + friend struct LLInitParam::ParamCompare; + + const LLUIColor* mColorPtr; + LLColor4 mColor; +}; + +namespace LLInitParam +{ + template<> + struct ParamCompare + { + static bool equals(const LLUIColor& a, const LLUIColor& b); + }; +} + +#endif diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e28151f62..96169caf2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -37,6 +37,7 @@ include(LLUI) include(LLVFS) include(LLWindow) include(LLXML) +include(LLXUIXML) include(LScript) include(Linking) include(NDOF) @@ -71,6 +72,7 @@ include_directories( ${LLVFS_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} + ${LLXUIXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile ) @@ -86,7 +88,6 @@ set(viewer_SOURCE_FILES lggdicdownload.cpp floaterao.cpp floatervoicelicense.cpp - cofmgr.cpp lldaycyclemanager.cpp llenvmanager.cpp llwlhandlers.cpp @@ -122,7 +123,9 @@ set(viewer_SOURCE_FILES llagentpilot.cpp llagentui.cpp llagentwearables.cpp + llagentwearablesfetch.cpp llanimstatelabels.cpp + llappearancemgr.cpp llappviewer.cpp llassetconverter.cpp llassetuploadresponders.cpp @@ -308,6 +311,7 @@ set(viewer_SOURCE_FILES llinventorypanel.cpp lljoystickbutton.cpp lllandmarklist.cpp + lllocaltextureobject.cpp lllocalinventory.cpp lllogchat.cpp llloginhandler.cpp @@ -329,6 +333,7 @@ set(viewer_SOURCE_FILES llnamelistctrl.cpp llnetmap.cpp llnotify.cpp + lloutfitobserver.cpp lloverlaybar.cpp llpanelaudioprefs.cpp llpanelaudiovolume.cpp @@ -357,7 +362,7 @@ set(viewer_SOURCE_FILES llpanelgrouproles.cpp llpanelgroupvoting.cpp llpanelinput.cpp - llpanelinventory.cpp + llpanelobjectinventory.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmedia.cpp @@ -411,7 +416,9 @@ set(viewer_SOURCE_FILES llstylemap.cpp llsurface.cpp llsurfacepatch.cpp + lltexglobalcolor.cpp lltexlayer.cpp + lltexlayerparams.cpp lltexturecache.cpp lltexturectrl.cpp lltexturefetch.cpp @@ -571,7 +578,6 @@ set(viewer_HEADER_FILES aoremotectrl.h floaterao.h floatervoicelicense.h - cofmgr.h lldaycyclemanager.h llenvmanager.h llwlhandlers.h @@ -607,8 +613,10 @@ set(viewer_HEADER_FILES llagentpilot.h llagentui.h llagentwearables.h + llagentwearablesfetch.h llanimstatelabels.h llappearance.h + llappearancemgr.h llappviewer.h llassetconverter.h llassetuploadresponders.h @@ -794,6 +802,7 @@ set(viewer_HEADER_FILES lljoystickbutton.h lllandmarklist.h lllightconstants.h + lllocaltextureobject.h lllocalinventory.h lllogchat.h llloginhandler.h @@ -815,6 +824,7 @@ set(viewer_HEADER_FILES llnamelistctrl.h llnetmap.h llnotify.h + lloutfitobserver.h lloverlaybar.h llpanelaudioprefs.h llpanelaudiovolume.h @@ -843,7 +853,7 @@ set(viewer_HEADER_FILES llpanelgrouproles.h llpanelgroupvoting.h llpanelinput.h - llpanelinventory.h + llpanelobjectinventory.h llpanelland.h llpanellandaudio.h llpanellandmedia.h @@ -900,7 +910,9 @@ set(viewer_HEADER_FILES llsurface.h llsurfacepatch.h lltable.h + lltexglobalcolor.h lltexlayer.h + lltexlayerparams.h lltexturecache.h lltexturectrl.h lltexturefetch.h @@ -1522,6 +1534,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLVFS_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} + ${LLXUIXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 040eb0664..1994642e5 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2076,6 +2076,28 @@ Value 2 + AvatarBakedTextureUploadTimeout + + Comment + Specifes the maximum time in seconds to wait before sending your baked textures for avatar appearance. Set to 0 to disable and wait until all baked textures are at highest resolution. + Persist + 1 + Type + U32 + Value + 60 + + AvatarBakedLocalTextureUpdateTimeout + + Comment + Specifes the maximum time in seconds to wait before updating your appearance during appearance mode. + Persist + 1 + Type + U32 + Value + 10 + AvatarPhysics Comment @@ -3994,8 +4016,30 @@ Type String Value - + + DebugAvatarRezTime + + Comment + Display times for avatars to resolve. + Persist + 1 + Type + Boolean + Value + 0 + + DebugAvatarLocalTexLoadedTime + + Comment + Display time for loading avatar local textures. + Persist + 1 + Type + Boolean + Value + 0 + DebugBeaconLineWidth Comment @@ -6709,6 +6753,17 @@ Value 0 + ForceAssetFail + + Comment + Force wearable fetches to fail for this asset type. + Persist + 1 + Type + U32 + Value + 255 + ForceShowGrid Comment @@ -7949,6 +8004,17 @@ Value 128.0 + MaxWearableWaitTime + + Comment + Max seconds to wait for wearable assets to fetch. + Persist + 1 + Type + F32 + Value + 60.0 + MeanCollisionBump Comment @@ -8409,8 +8475,19 @@ Type Boolean Value - 1 + 0 + MyOutfitsAutofill + + Comment + Always autofill My Outfits from library when empty (else happens just once). + Persist + 1 + Type + Boolean + Value + 0 + NearMeRange Comment @@ -14694,6 +14771,17 @@ Value 1 + OutfitOperationsTimeout + + Comment + Timeout for outfit related operations. + Persist + 1 + Type + S32 + Value + 180 + UseHTTPInventory Comment diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp index 4b73e2e1f..245a33af2 100644 --- a/indra/newview/ascentprefsvan.cpp +++ b/indra/newview/ascentprefsvan.cpp @@ -98,13 +98,11 @@ void LLPrefsAscentVan::onCommitClientTag(LLUICtrl* ctrl, void* userdata) gSavedSettings.setString("AscentReportClientUUID", client_uuid); gSavedSettings.setU32("AscentReportClientIndex", client_index); - LLVOAvatar* avatar = gAgentAvatarp; - - if (avatar) + if (gAgentAvatarp) { // Slam pending upload count to "unstick" things bool slam_for_debug = true; - avatar->forceBakeAllTextures(slam_for_debug); + gAgentAvatarp->forceBakeAllTextures(slam_for_debug); } } } diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index 4ad68ee8d..6d659e2d0 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -6387,8 +6387,6 @@ - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/indra/newview/cofmgr.cpp b/indra/newview/cofmgr.cpp deleted file mode 100644 index 2b7da5ddf..000000000 --- a/indra/newview/cofmgr.cpp +++ /dev/null @@ -1,536 +0,0 @@ -/** - * - * Copyright (c) 2010, Kitty Barnett - * - * The source code in this file is provided to you under the terms of the - * GNU General Public License, version 2.0, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. Terms of the GPL can be found in doc/GPL-license.txt - * in this distribution, or online at http://www.gnu.org/licenses/gpl-2.0.txt - * - * 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. - * - */ - -#include "llviewerprecompiledheaders.h" -#include "cofmgr.h" -#include "llagent.h" -#include "llagentwearables.h" -#include "llcommonutils.h" -#include "llerror.h" -#include "llinventoryfunctions.h" -#include "llinventoryobserver.h" -#include "llvoavatarself.h" -#include "rlvviewer2.h" - -// ============================================================================ -// Inventory helper classes -// - -class LLCOFLinkTargetFetcher : public LLInventoryFetchItemsObserver -{ -public: - LLCOFLinkTargetFetcher(const uuid_vec_t& idItems) : LLInventoryFetchItemsObserver(idItems) {} - /*virtual*/ ~LLCOFLinkTargetFetcher() {} - - /*virtual*/ void done() - { - // We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers() - doOnIdleOneTime(boost::bind(&LLCOFLinkTargetFetcher::doneIdle, this)); - gInventory.removeObserver(this); - } - - void doneIdle() - { - LLCOFMgr::instance().checkCOF(); - LLCOFMgr::instance().setLinkAttachments(true); - LLCOFMgr::instance().updateAttachments(); - LLCOFMgr::instance().synchWearables(); - - delete this; - } -}; - -class LLCOFFetcher : public LLInventoryFetchDescendentsObserver -{ -public: - LLCOFFetcher(const LLUUID& cat_id) : LLInventoryFetchDescendentsObserver(cat_id) {} - /*virtual*/ ~LLCOFFetcher() {} - - /*virtual*/ void done() - { - // We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers() - doOnIdleOneTime(boost::bind(&LLCOFFetcher::doneIdle, this)); - gInventory.removeObserver(this); - } - - void doneIdle() - { - uuid_vec_t idItems; - - // Add the link targets for COF - LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items; - gInventory.collectDescendents(LLCOFMgr::getCOF(), folders, items, LLInventoryModel::EXCLUDE_TRASH); - for (S32 idxItem = 0; idxItem < items.count(); idxItem++) - { - const LLViewerInventoryItem* pItem = items.get(idxItem); - if (!pItem) - continue; - idItems.push_back(pItem->getLinkedUUID()); - } - - // Add all currently worn wearables - for (S32 idxType = 0; idxType < LLWearableType::WT_COUNT; idxType++) - { - const LLUUID& idItem = gAgentWearables.getWearableItemID((LLWearableType::EType)idxType,0); // TODO: MULTI-WEARABLE - if (idItem.isNull()) - continue; - idItems.push_back(idItem); - } - - // Add all currently worn attachments - const LLVOAvatar* pAvatar = gAgentAvatarp; - if (pAvatar) - { - for (LLVOAvatar::attachment_map_t::const_iterator itAttachPt = pAvatar->mAttachmentPoints.begin(); - itAttachPt != pAvatar->mAttachmentPoints.end(); ++itAttachPt) - { - const LLViewerJointAttachment* pAttachPt = itAttachPt->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator itAttachment = pAttachPt->mAttachedObjects.begin(); - itAttachment != pAttachPt->mAttachedObjects.end(); ++itAttachment) - { - const LLUUID& idItem = (*itAttachment)->getAttachmentItemID(); - if (idItem.isNull()) - continue; - idItems.push_back(idItem); - } - } - } - - // Fetch it all - LLCOFLinkTargetFetcher* pFetcher = new LLCOFLinkTargetFetcher(idItems); - pFetcher->startFetch(); - if (pFetcher->isFinished()) - { - pFetcher->done(); - } - else - { - gInventory.addObserver(pFetcher); - - // It doesn't look like we *really* need the link targets so we can do a preliminary initialization now already - LLCOFMgr::instance().setLinkAttachments(true); - LLCOFMgr::instance().updateAttachments(); - } - - delete this; - } -}; - -class LLDeferredAddLinkTargetFetcher : public LLInventoryFetchItemsObserver -{ -public: - LLDeferredAddLinkTargetFetcher(const LLUUID& idItem, LLPointer cb) - : LLInventoryFetchItemsObserver(idItem), m_Callback(cb) - {} - LLDeferredAddLinkTargetFetcher(const uuid_vec_t& idItems, LLPointer cb) - : LLInventoryFetchItemsObserver(idItems), m_Callback(cb) - {} - /*virtual*/ ~LLDeferredAddLinkTargetFetcher() {} - - /*virtual*/ void done() - { - // We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers() - doOnIdleOneTime(boost::bind(&LLDeferredAddLinkTargetFetcher::doneIdle, this)); - gInventory.removeObserver(this); - } - - void doneIdle() - { - for (uuid_vec_t::const_iterator itItemId = mComplete.begin(); itItemId != mComplete.end(); ++itItemId) - { - const LLViewerInventoryItem* pItem = gInventory.getItem(*itItemId); - if (!pItem) - continue; - LLCOFMgr::instance().addCOFItemLink(pItem, m_Callback); - } - delete this; - } -protected: - LLPointer m_Callback; -}; - -class LLLinkAttachmentCallback : public LLInventoryCallback -{ -public: - LLLinkAttachmentCallback() {} - /*virtual*/ ~LLLinkAttachmentCallback() {} - /*virtual*/ void fire(const LLUUID& idItem) { LLCOFMgr::instance().onLinkAttachmentComplete(idItem); } -}; - -class LLLinkWearableCallback : public LLInventoryCallback -{ -public: - LLLinkWearableCallback() {} - /*virtual*/ ~LLLinkWearableCallback() {} - /*virtual*/ void fire(const LLUUID& idItem) { LLCOFMgr::instance().onLinkWearableComplete(idItem); } -}; - -// ============================================================================ -// Helper functions -// - -void LLCOFMgr::checkCOF() -{ - const LLUUID idCOF = getCOF(); - const LLUUID idLAF = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - - // Check COF for non-links and move them to Lost&Found - LLInventoryModel::cat_array_t* pFolders; LLInventoryModel::item_array_t* pItems; - gInventory.getDirectDescendentsOf(idCOF, pFolders, pItems); - for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++) - { - LLViewerInventoryCategory* pFolder = pFolders->get(idxFolder).get(); - if ( (pFolder) && (idLAF.notNull()) ) - change_category_parent(&gInventory, pFolder, idLAF, false); - } - for (S32 idxItem = 0, cntItem = pItems->count(); idxItem < cntItem; idxItem++) - { - LLViewerInventoryItem* pItem = pItems->get(idxItem).get(); - if ( (pItem) && (!pItem->getIsLinkType()) && (idLAF.notNull()) ) - change_item_parent(&gInventory, pItem, idLAF, false); - } -} - -void LLCOFMgr::fetchCOF() -{ - static bool sFetched = false; - if (!sFetched) - { - const LLUUID idCOF = getCOF(); - if (idCOF.isNull()) - { - LL_ERRS("COFMgr") << "Unable to find (or create) COF" << LL_ENDL; - return; - } - - LLCOFFetcher* pFetcher = new LLCOFFetcher(idCOF); - pFetcher->startFetch(); - if (pFetcher->isFinished()) - pFetcher->done(); - else - gInventory.addObserver(pFetcher); - - sFetched = true; - } -} - -void LLCOFMgr::getDescendentsOfAssetType(const LLUUID& idCat, LLInventoryModel::item_array_t& items, - LLAssetType::EType typeAsset, bool fFollowFolderLinks) -{ - LLInventoryModel::cat_array_t folders; - LLIsType f(typeAsset); - gInventory.collectDescendentsIf(idCat, folders, items, LLInventoryModel::EXCLUDE_TRASH, f, fFollowFolderLinks); -} - -void LLCOFMgr::addCOFItemLink(const LLUUID& idItem, LLPointer cb) -{ - const LLViewerInventoryItem *pItem = gInventory.getItem(idItem); - if (!pItem) - { - LLDeferredAddLinkTargetFetcher* pFetcher = new LLDeferredAddLinkTargetFetcher(idItem, cb); - pFetcher->startFetch(); - gInventory.addObserver(pFetcher); - return; - } - addCOFItemLink(pItem, cb); -} - -void LLCOFMgr::addCOFItemLink(const LLInventoryItem* pItem, LLPointer cb) -{ - if ( (!pItem) || (isLinkInCOF(pItem->getLinkedUUID())) ) - { - return; - } - - gInventory.addChangedMask(LLInventoryObserver::LABEL, pItem->getLinkedUUID()); - - const std::string strDescr = (pItem->getIsLinkType()) ? pItem->getDescription() : ""; - link_inventory_item(gAgent.getID(), pItem->getLinkedUUID(), getCOF(), pItem->getName(), strDescr, LLAssetType::AT_LINK, cb); -} - -bool LLCOFMgr::isLinkInCOF(const LLUUID& idItem) const -{ - LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items; - LLLinkedItemIDMatches f(gInventory.getLinkedItemID(idItem)); - gInventory.collectDescendentsIf(getCOF(), folders, items, LLInventoryModel::EXCLUDE_TRASH, f); - return (!items.empty()); -} - -void LLCOFMgr::removeCOFItemLinks(const LLUUID& idItem) -{ - gInventory.addChangedMask(LLInventoryObserver::LABEL, idItem); - - LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items; - gInventory.collectDescendents(getCOF(), folders, items, LLInventoryModel::EXCLUDE_TRASH); - - for (S32 idxItem = 0; idxItem < items.count(); idxItem++) - { - const LLInventoryItem* pItem = items.get(idxItem).get(); - if ( (pItem->getIsLinkType()) && (idItem == pItem->getLinkedUUID()) ) - gInventory.purgeObject(pItem->getUUID()); - } -} - -BOOL LLCOFMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const -{ - if (!isLinkInCOF(obj_id)) return FALSE; - - // If a non-link somehow ended up in COF, allow deletion. - const LLInventoryObject *obj = gInventory.getObject(obj_id); - if (obj && !obj->getIsLinkType()) - { - return FALSE; - } - - // For now, don't allow direct deletion from the COF. Instead, force users - // to choose "Detach" or "Take Off". - return TRUE; - /* - const LLInventoryObject *obj = gInventory.getObject(obj_id); - if (!obj) return FALSE; - - // Can't delete bodyparts, since this would be equivalent to removing the item. - if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE; - - // Can't delete the folder link, since this is saved for bookkeeping. - if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE; - - return FALSE; - */ -} - -// ============================================================================ -// Attachment functions -// - -void LLCOFMgr::addAttachment(const LLUUID& idItem) -{ - if ( (isLinkInCOF(idItem)) || (std::find(m_PendingAttachLinks.begin(), m_PendingAttachLinks.end(), idItem) != m_PendingAttachLinks.end()) ) - { - return; - } - - gInventory.addChangedMask(LLInventoryObserver::LABEL, idItem); - m_PendingAttachLinks.push_back(idItem); - - if (m_fLinkAttachments) - { - LLPointer cb = new LLLinkAttachmentCallback(); - addCOFItemLink(idItem, cb); - } -} - -void LLCOFMgr::removeAttachment(const LLUUID& idItem) -{ - gInventory.addChangedMask(LLInventoryObserver::LABEL, idItem); - - // Remove the attachment from the pending list - uuid_vec_t::iterator itPending = std::find(m_PendingAttachLinks.begin(), m_PendingAttachLinks.end(), idItem); - if (itPending != m_PendingAttachLinks.end()) - m_PendingAttachLinks.erase(itPending); - - if (m_fLinkAttachments) - { - removeCOFItemLinks(idItem); - } -} - -void LLCOFMgr::setLinkAttachments(bool fEnable) -{ - m_fLinkAttachments = fEnable; - - if (m_fLinkAttachments) - linkPendingAttachments(); -} - -void LLCOFMgr::linkPendingAttachments() -{ - LLPointer cb = NULL; - for (uuid_vec_t::const_iterator itPending = m_PendingAttachLinks.begin(); itPending != m_PendingAttachLinks.end(); ++itPending) - { - const LLUUID& idAttachItem = *itPending; - if ( (gAgentAvatarp->isWearingAttachment(idAttachItem)) && (!isLinkInCOF(idAttachItem)) ) - { - if (!cb) - cb = new LLLinkAttachmentCallback(); - addCOFItemLink(idAttachItem, cb); - } - } -} - -void LLCOFMgr::onLinkAttachmentComplete(const LLUUID& idItem) -{ - const LLUUID& idItemBase = gInventory.getLinkedItemID(idItem); - - // Remove the attachment from the pending list - uuid_vec_t::iterator itPending = std::find(m_PendingAttachLinks.begin(), m_PendingAttachLinks.end(), idItemBase); - if (itPending != m_PendingAttachLinks.end()) - m_PendingAttachLinks.erase(itPending); - - // It may have been detached already in which case we should remove the COF link - if ( (gAgentAvatarp) && (!gAgentAvatarp->isWearingAttachment(idItemBase)) ) - removeCOFItemLinks(idItemBase); -} - -void LLCOFMgr::updateAttachments() -{ - /*const*/ LLVOAvatar* pAvatar = gAgentAvatarp; - if (!pAvatar) - return; - - const LLUUID idCOF = getCOF(); - - // Grab all attachment links currently in COF - LLInventoryModel::item_array_t items; - getDescendentsOfAssetType(idCOF, items, LLAssetType::AT_OBJECT, true); - - // Include attachments which should be in COF but don't have their link created yet - uuid_vec_t::iterator itPendingAttachLink = m_PendingAttachLinks.begin(); - while (itPendingAttachLink != m_PendingAttachLinks.end()) - { - const LLUUID& idItem = *itPendingAttachLink; - if ( (!pAvatar->isWearingAttachment(idItem)) || (isLinkInCOF(idItem)) ) - { - itPendingAttachLink = m_PendingAttachLinks.erase(itPendingAttachLink); - continue; - } - - LLViewerInventoryItem* pItem = gInventory.getItem(idItem); - if (pItem) - items.push_back(pItem); - - ++itPendingAttachLink; - } - - // Don't remove attachments until avatar is fully loaded (should reduce random attaching/detaching/reattaching at log-on) - LLAgentWearables::userUpdateAttachments(items, !pAvatar->isFullyLoaded()); -} - -// ============================================================================ -// Wearable functions -// - -void LLCOFMgr::addWearable(const LLUUID& idItem) -{ - if ( (isLinkInCOF(idItem)) || (std::find(m_PendingWearableLinks.begin(), m_PendingWearableLinks.end(), idItem) != m_PendingWearableLinks.end()) ) - { - return; - } - - gInventory.addChangedMask(LLInventoryObserver::LABEL, idItem); - m_PendingWearableLinks.push_back(idItem); - - LLPointer cb = new LLLinkWearableCallback(); - addCOFItemLink(idItem, cb); -} - -void LLCOFMgr::removeWearable(const LLUUID& idItem) -{ - gInventory.addChangedMask(LLInventoryObserver::LABEL, idItem); - - // Remove the attachment from the pending list - uuid_vec_t::iterator itPending = std::find(m_PendingWearableLinks.begin(), m_PendingWearableLinks.end(), idItem); - if (itPending != m_PendingWearableLinks.end()) - m_PendingWearableLinks.erase(itPending); - - removeCOFItemLinks(idItem); -} - -void LLCOFMgr::onLinkWearableComplete(const LLUUID& idItem) -{ - const LLUUID& idItemBase = gInventory.getLinkedItemID(idItem); - - // Remove the attachment from the pending list - uuid_vec_t::iterator itPending = std::find(m_PendingWearableLinks.begin(), m_PendingWearableLinks.end(), idItemBase); - if (itPending != m_PendingWearableLinks.end()) - m_PendingWearableLinks.erase(itPending); - - // It may have been removed already in which case we should remove the COF link - if (!gAgentWearables.isWearingItem(idItemBase)) - removeCOFItemLinks(idItemBase); -} - -void LLCOFMgr::synchWearables() -{ - const LLUUID idCOF = getCOF(); - - // Grab all wearable links currently in COF - LLInventoryModel::item_array_t items; - getDescendentsOfAssetType(idCOF, items, LLAssetType::AT_BODYPART, true); - getDescendentsOfAssetType(idCOF, items, LLAssetType::AT_CLOTHING, true); - - // Transform collection of link LLViewerInventory pointers into collection of target LLUUIDs - uuid_vec_t curItems; - for (S32 idxItem = 0; idxItem < items.count(); idxItem++) - { - const LLViewerInventoryItem* pItem = items.get(idxItem); - if (!pItem) - continue; - curItems.push_back(pItem->getLinkedUUID()); - } - - // Grab the item UUIDs of all currently worn wearables - uuid_vec_t newItems; - for (S32 idxType = 0; idxType < LLWearableType::WT_COUNT; idxType++) - { - const LLUUID& idItem = gAgentWearables.getWearableItemID((LLWearableType::EType)idxType, 0); // TODO: MULTI-WEARABLE - if (idItem.isNull()) - continue; - newItems.push_back(idItem); - } - - uuid_vec_t addItems, remItems; - LLCommonUtils::computeDifference(newItems, curItems, addItems, remItems); - - // Add links for worn wearables that aren't linked yet - for (uuid_vec_t::const_iterator itItem = addItems.begin(); itItem != addItems.end(); ++itItem) - addWearable(*itItem); - - // Remove links of wearables that aren't worn anymore - for (uuid_vec_t::const_iterator itItem = remItems.begin(); itItem != remItems.end(); ++itItem) - removeWearable(*itItem); -} - -// ============================================================================ -// Base outfit folder functions -// - -void LLCOFMgr::addBOFLink(const LLUUID &idFolder, LLPointer cb) -{ - purgeBOFLink(); - - const LLViewerInventoryCategory* pFolder = gInventory.getCategory(idFolder); - if ( (pFolder) && (LLFolderType::FT_OUTFIT == pFolder->getPreferredType()) ) - link_inventory_item(gAgent.getID(), idFolder, getCOF(), pFolder->getName(), "", LLAssetType::AT_LINK_FOLDER, cb); -} - -void LLCOFMgr::purgeBOFLink() -{ - LLInventoryModel::cat_array_t* pFolders; LLInventoryModel::item_array_t* pItems; - gInventory.getDirectDescendentsOf(getCOF(), pFolders, pItems); - for (S32 idxItem = 0, cntItem = pItems->count(); idxItem < cntItem; idxItem++) - { - const LLViewerInventoryItem* pItem = pItems->get(idxItem).get(); - if ( (!pItem) || (LLAssetType::AT_LINK_FOLDER != pItem->getActualType()) ) - continue; - - const LLViewerInventoryCategory* pLinkFolder = pItem->getLinkedCategory(); - if ( (pLinkFolder) && (LLFolderType::FT_OUTFIT == pLinkFolder->getPreferredType()) ) - gInventory.purgeObject(pItem->getUUID()); - } -} - -// ============================================================================ diff --git a/indra/newview/cofmgr.h b/indra/newview/cofmgr.h deleted file mode 100644 index 50d812c68..000000000 --- a/indra/newview/cofmgr.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * - * Copyright (c) 2010, Kitty Barnett - * - * The source code in this file is provided to you under the terms of the - * GNU General Public License, version 2.0, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. Terms of the GPL can be found in doc/GPL-license.txt - * in this distribution, or online at http://www.gnu.org/licenses/gpl-2.0.txt - * - * 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. - * - */ - -#include "llinventorymodel.h" -#include "llmemory.h" -#include "llviewerinventory.h" - -// ============================================================================ - -class LLCOFMgr : public LLSingleton -{ - /* - * Helper functions - */ -public: - void checkCOF(); - void fetchCOF(); - static const LLUUID getCOF() { return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); } - static void getDescendentsOfAssetType(const LLUUID& idCat, LLInventoryModel::item_array_t& items, - LLAssetType::EType typeAsset, bool fFollowFolderLinks); - bool isLinkInCOF(const LLUUID& idItem) const; - BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const; -protected: - void addCOFItemLink(const LLUUID& idItem, LLPointer cb = NULL); - void addCOFItemLink(const LLInventoryItem* pItem, LLPointer cb = NULL); - void removeCOFItemLinks(const LLUUID& idItem); - - /* - * Attachment functions - */ -public: - void addAttachment(const LLUUID& idItem); - void removeAttachment(const LLUUID& idItem); - void synchAttachments(); -protected: - void onLinkAttachmentComplete(const LLUUID& idItem); - void linkPendingAttachments(); - void setLinkAttachments(bool fEnable); - void updateAttachments(); - - /* - * Wearable functions - */ -public: - void addWearable(const LLUUID& idItem); - void removeWearable(const LLUUID& idItem); - void synchWearables(); -protected: - void onLinkWearableComplete(const LLUUID& idItem); - - /* - * Base outfit folder functions - */ -public: - void addBOFLink(const LLUUID& idFolder, LLPointer cb = NULL); - void purgeBOFLink(); - - /* - * Member variables - */ -protected: - bool m_fLinkAttachments; - uuid_vec_t m_PendingAttachLinks; - uuid_vec_t m_PendingWearableLinks; - -protected: - LLCOFMgr() : m_fLinkAttachments(false) {} -private: - friend class LLCOFFetcher; - friend class LLCOFLinkTargetFetcher; - friend class LLDeferredAddLinkTargetFetcher; - friend class LLLinkAttachmentCallback; - friend class LLLinkWearableCallback; - friend class LLSingleton; -}; - -// ============================================================================ diff --git a/indra/newview/dohexeditor.cpp b/indra/newview/dohexeditor.cpp index 071ff7d88..046360785 100644 --- a/indra/newview/dohexeditor.cpp +++ b/indra/newview/dohexeditor.cpp @@ -81,10 +81,6 @@ DOHexEditor::DOHexEditor(const std::string& name, const LLRect& rect) DOHexEditor::~DOHexEditor() { gFocusMgr.releaseFocusIfNeeded(this); - if(gEditMenuHandler == this) - { - gEditMenuHandler = NULL; - } } LLView* DOHexEditor::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory) @@ -273,7 +269,8 @@ U32 DOHexEditor::getProperSelectionEnd() BOOL DOHexEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) { - return mScrollbar->handleScrollWheel( 0, 0, clicks ); + mScrollbar->handleScrollWheel( 0, 0, clicks ); + return TRUE; } BOOL DOHexEditor::handleMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/newview/floaterao.cpp b/indra/newview/floaterao.cpp index cff0dbe23..afc0fcb75 100644 --- a/indra/newview/floaterao.cpp +++ b/indra/newview/floaterao.cpp @@ -30,7 +30,7 @@ #include "roles_constants.h" #include "llviewerregion.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llinventorybridge.h" #include "llboost.h" diff --git a/indra/newview/floaterlocalassetbrowse.cpp b/indra/newview/floaterlocalassetbrowse.cpp index 5c67eea15..eeb1ebfd4 100644 --- a/indra/newview/floaterlocalassetbrowse.cpp +++ b/indra/newview/floaterlocalassetbrowse.cpp @@ -670,8 +670,10 @@ void LocalAssetBrowser::PerformTimedActions(void) // one of the layer bitmaps has been updated, we need to rebake. if ( mLayerUpdated ) { - LLVOAvatar* avatar = gAgentAvatarp; - if (avatar) { avatar->forceBakeAllTextures(SLAM_FOR_DEBUG); } + if (isAgentAvatarValid()) + { + gAgentAvatarp->forceBakeAllTextures(SLAM_FOR_DEBUG); + } mLayerUpdated = false; } diff --git a/indra/newview/floatersculptpreview.cpp b/indra/newview/floatersculptpreview.cpp index 09356154d..cc8706e5c 100644 --- a/indra/newview/floatersculptpreview.cpp +++ b/indra/newview/floatersculptpreview.cpp @@ -259,7 +259,7 @@ void LLFloaterSculptPreview::draw() if (selected <= 0) { - gl_rect_2d_checkerboard(getScreenRect(),mPreviewRect); + gl_rect_2d_checkerboard(calcScreenRect(),mPreviewRect); LLGLDisable gls_alpha(GL_ALPHA_TEST); if(mImagep.notNull()) diff --git a/indra/newview/hippofloaterxml.cpp b/indra/newview/hippofloaterxml.cpp index 477f0d7bb..c49788692 100644 --- a/indra/newview/hippofloaterxml.cpp +++ b/indra/newview/hippofloaterxml.cpp @@ -65,6 +65,11 @@ class HippoFloaterXmlImpl : public LLFloater private: std::string mName; bool mIsNotifyOnClose; + //Must retain handles to unregister notices. + typedef boost::signals2::scoped_connection notice_connection_t; + typedef boost::shared_ptr notice_ptr_t; + + std::map mNotices; }; @@ -260,7 +265,7 @@ void HippoFloaterXml::execute(const std::string &cmds) // ******************************************************************** // generic notification callbacks -static void notify(LLUICtrl *ctrl, void *data) +static void notifyCallback(LLUICtrl *ctrl) { std::string msg = "NOTIFY:"; msg += ctrl->getName(); @@ -269,11 +274,6 @@ static void notify(LLUICtrl *ctrl, void *data) send_chat_from_viewer(msg, CHAT_TYPE_WHISPER, CHANNEL); } -static void notify(void *data) -{ - notify(static_cast(data), 0); -} - void HippoFloaterXmlImpl::onClose(bool quitting) { if (mIsNotifyOnClose) @@ -324,16 +324,19 @@ bool HippoFloaterXmlImpl::execute(LLUICtrl *ctrl, bool set = (value != "0"); if (HippoFloaterXmlImpl *floater = dynamic_cast(ctrl)) { floater->mIsNotifyOnClose = set; - } else if (LLButton *button = dynamic_cast(ctrl)) { + } else if (LLButton *button = dynamic_cast(ctrl)) + { if (set) - button->setClickedCallback(notify, ctrl); + floater->mNotices[button] = notice_ptr_t(new notice_connection_t(button->setClickedCallback(boost::bind(¬ifyCallback, ctrl)))); else - button->setClickedCallback(0, 0); - } else { + floater->mNotices.erase(button); + } + else + { if (set) - ctrl->setCommitCallback(notify); + floater->mNotices[ctrl] = notice_ptr_t(new notice_connection_t(ctrl->setCommitCallback(boost::bind(¬ifyCallback, ctrl)))); else - ctrl->setCommitCallback(0); + floater->mNotices.erase(ctrl); } } } diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index b618efa72..f2150ae2d 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3698,12 +3698,6 @@ void LLAgent::setTeleportState(ETeleportState state) default: break; } -// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Version: 1.23.4 | Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.0b - if ( (rlv_handler_t::isEnabled()) && (TELEPORT_NONE == mTeleportState) ) - { - gRlvHandler.setCanCancelTp(true); - } -// [/RLVa:KB] } void LLAgent::stopCurrentAnimations() @@ -3840,12 +3834,11 @@ void LLAgent::sendAgentSetAppearance() { if (!isAgentAvatarValid()) return; - if (gAgentQueryManager.mNumPendingQueries > 0 && (isAgentAvatarValid() && !gAgentCamera.cameraCustomizeAvatar())) + if (gAgentQueryManager.mNumPendingQueries > 0 && (isAgentAvatarValid() && gAgentAvatarp->isUsingBakedTextures())) { return; } - llinfos << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << llendl; //dumpAvatarTEs( "sendAgentSetAppearance()" ); @@ -3875,7 +3868,7 @@ void LLAgent::sendAgentSetAppearance() // is texture data current relative to wearables? // KLW - TAT this will probably need to check the local queue. - BOOL textures_current = !gAgentAvatarp->hasPendingBakedUploads() && gAgentWearables.areWearablesLoaded(); + BOOL textures_current = gAgentAvatarp->areTexturesCurrent(); for(U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++ ) { @@ -3887,8 +3880,8 @@ void LLAgent::sendAgentSetAppearance() continue; } - // IMG_DEFAULT_AVATAR means not baked - if (!gAgentAvatarp->isTextureDefined(texture_index)) + // IMG_DEFAULT_AVATAR means not baked. 0 index should be ignored for baked textures + if (!gAgentAvatarp->isTextureDefined(texture_index, 0)) { textures_current = FALSE; break; @@ -3901,12 +3894,17 @@ void LLAgent::sendAgentSetAppearance() llinfos << "TAT: Sending cached texture data" << llendl; for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { + BOOL generate_valid_hash = TRUE; + if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLVOAvatarDefines::EBakedTextureIndex)baked_index)) + { + generate_valid_hash = FALSE; + llinfos << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << llendl; + } - const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, true); + const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash); if (hash.notNull()) { - const ETextureIndex texture_index = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)baked_index); - + ETextureIndex texture_index = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex) baked_index); msg->nextBlockFast(_PREHASH_WearableData); msg->addUUIDFast(_PREHASH_CacheID, hash); msg->addU8Fast(_PREHASH_TextureIndex, (U8)texture_index); diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 06c52894a..5a18f0ea3 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -2392,7 +2392,7 @@ void LLAgentCamera::changeCameraToCustomizeAvatar(BOOL avatar_animate, BOOL came gFocusMgr.setKeyboardFocus( NULL ); gFocusMgr.setMouseCapture( NULL ); - LLVOAvatar::onCustomizeStart(); + LLVOAvatarSelf::onCustomizeStart(); if (isAgentAvatarValid()) { @@ -2418,10 +2418,11 @@ void LLAgentCamera::changeCameraToCustomizeAvatar(BOOL avatar_animate, BOOL came { setAnimationDuration(gSavedSettings.getF32("ZoomTime")); } - //gAgentAvatarp->invalidateAll(); - //gAgentAvatarp->updateMeshTextures(); } + gAgentAvatarp->invalidateAll(); + gAgentAvatarp->updateMeshTextures(); + gAgentCamera.setFocusGlobal(LLVector3d::zero); } } diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index a583d771a..a76b0215e 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -36,21 +36,26 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llagentwearablesfetch.h" +#include "llappearancemgr.h" #include "llcallbacklist.h" #include "llfolderview.h" #include "llgesturemgr.h" #include "llinventorybridge.h" -#include "llfloaterinventory.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" #include "llinventorypanel.h" +#include "llpanelmaininventory.h" #include "llmd5.h" #include "llnotificationsutil.h" +#include "lloutfitobserver.h" #include "lltexlayer.h" +#include "lltooldraganddrop.h" #include "llviewerregion.h" #include "llvoavatarself.h" #include "llwearable.h" #include "llwearablelist.h" -#include "cofmgr.h" #include "llfloatercustomize.h" @@ -69,8 +74,110 @@ LLAgentWearables gAgentWearables; BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE; using namespace LLVOAvatarDefines; + +/////////////////////////////////////////////////////////////////////////////// + +// Callback to wear and start editing an item that has just been created. +class LLWearAndEditCallback : public LLInventoryCallback +{ + void fire(const LLUUID& inv_item) + { + if (inv_item.isNull()) return; + + // Request editing the item after it gets worn. + gAgentWearables.requestEditingWearable(inv_item); + + // Wear it. + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +// HACK: For EXT-3923: Pants item shows in inventory with skin icon and messes with "current look" +// Some db items are corrupted, have inventory flags = 0, implying wearable type = shape, even though +// wearable type stored in asset is some other value. +// Calling this function whenever a wearable is added to increase visibility if this problem +// turns up in other inventories. +void checkWearableAgainstInventory(LLWearable *wearable) +{ + if (wearable->getItemID().isNull()) + return; + + // Check for wearable type consistent with inventory item wearable type. + LLViewerInventoryItem *item = gInventory.getItem(wearable->getItemID()); + if (item) + { + if (!item->isWearableType()) + { + llwarns << "wearable associated with non-wearable item" << llendl; + } + if (item->getWearableType() != wearable->getType()) + { + llwarns << "type mismatch: wearable " << wearable->getName() + << " has type " << wearable->getType() + << " but inventory item " << item->getName() + << " has type " << item->getWearableType() << llendl; + } + } + else + { + llwarns << "wearable inventory item not found" << wearable->getName() + << " itemID " << wearable->getItemID().asString() << llendl; + } +} + +void LLAgentWearables::dump() +{ + llinfos << "LLAgentWearablesDump" << llendl; + for (S32 i = 0; i < LLWearableType::WT_COUNT; i++) + { + U32 count = getWearableCount((LLWearableType::EType)i); + llinfos << "Type: " << i << " count " << count << llendl; + for (U32 j=0; jgetName() + << " description " << wearable->getDescription() << llendl; + + } + } + llinfos << "Total items awaiting wearable update " << mItemsAwaitingWearableUpdate.size() << llendl; + for (std::set::iterator it = mItemsAwaitingWearableUpdate.begin(); + it != mItemsAwaitingWearableUpdate.end(); + ++it) + { + llinfos << (*it).asString() << llendl; + } +} + +struct LLAgentDumper +{ + LLAgentDumper(std::string name): + mName(name) + { + llinfos << llendl; + llinfos << "LLAgentDumper " << mName << llendl; + gAgentWearables.dump(); + } + + ~LLAgentDumper() + { + llinfos << llendl; + llinfos << "~LLAgentDumper " << mName << llendl; + gAgentWearables.dump(); + } + + std::string mName; +}; + LLAgentWearables::LLAgentWearables() : mWearablesLoaded(FALSE) +, mCOFChangeInProgress(false) { } @@ -86,15 +193,20 @@ void LLAgentWearables::cleanup() // static void LLAgentWearables::initClass() { + // this can not be called from constructor because its instance is global and is created too early. + // Subscribe to "COF is Saved" signal to notify observers about this (Loading indicator for ex.). + LLOutfitObserver::instance().addCOFSavedCallback(boost::bind(&LLAgentWearables::notifyLoadingFinished, &gAgentWearables)); } void LLAgentWearables::setAvatarObject(LLVOAvatarSelf *avatar) { if (avatar) { + avatar->outputRezTiming("Sending wearables request"); sendAgentWearablesRequest(); } } + // wearables LLAgentWearables::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback() { @@ -125,7 +237,7 @@ LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInvento mTodo(todo), mCB(cb) { - llassert_always(index == 0); + //llassert_always(index == 0); llinfos << "constructor" << llendl; } @@ -147,6 +259,7 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i } if (mTodo & CALL_RECOVERDONE) { + LLAppearanceMgr::instance().addCOFItemLink(inv_item,false); gAgentWearables.recoverMissingWearableDone(); } /* @@ -154,12 +267,17 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i */ if (mTodo & CALL_CREATESTANDARDDONE) { + LLAppearanceMgr::instance().addCOFItemLink(inv_item,false); gAgentWearables.createStandardWearablesDone(mType, mIndex); } if (mTodo & CALL_MAKENEWOUTFITDONE) { gAgentWearables.makeNewOutfitDone(mType, mIndex); } + if (mTodo & CALL_WEARITEM) + { + LLAppearanceMgr::instance().addCOFItemLink(inv_item, true); + } } void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::EType type, @@ -167,7 +285,7 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::ETy const LLUUID& item_id, LLWearable* wearable) { - llassert_always(index == 0); + //llassert_always(index == 0); llinfos << "type " << type << " index " << index << " item " << item_id.asString() << llendl; @@ -240,9 +358,6 @@ void LLAgentWearables::sendAgentWearablesUpdate() // Then make sure the inventory is in sync with the avatar. gInventory.notifyObservers(); - // This isn't the proper place to be doing this, but it's a good "catch-all" - LLCOFMgr::instance().synchWearables(); - // Send the AgentIsNowWearing gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing); @@ -287,19 +402,24 @@ void LLAgentWearables::sendAgentWearablesUpdate() void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32 index, BOOL send_update, const std::string new_name) { - llassert_always(index == 0); + //llassert_always(index == 0); LLWearable* old_wearable = getWearable(type, index); if(!old_wearable) return; bool name_changed = !new_name.empty() && (new_name != old_wearable->getName()); if (name_changed || old_wearable->isDirty() || old_wearable->isOldVersion()) { LLUUID old_item_id = old_wearable->getItemID(); - LLWearable* new_wearable = LLWearableList::instance().createCopyFromAvatar( old_wearable ); + LLWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); new_wearable->setItemID(old_item_id); // should this be in LLWearable::copyDataFrom()? setWearable(type,index,new_wearable); + // old_wearable may still be referred to by other inventory items. Revert + // unsaved changes so other inventory items aren't affected by the changes + // that were just saved. + old_wearable->revertValues(); + LLInventoryItem* item = gInventory.getItem(old_item_id); - if( item ) + if (item) { std::string item_name = item->getName(); if (name_changed) @@ -361,7 +481,7 @@ void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, const std::string& new_name, BOOL save_in_lost_and_found) { - llassert_always(index == 0); + //llassert_always(index == 0); if (!isWearableCopyable(type, index)) { llwarns << "LLAgent::saveWearableAs() not copyable." << llendl; @@ -382,7 +502,7 @@ void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, } std::string trunc_name(new_name); LLStringUtil::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN); - LLWearable* new_wearable = LLWearableList::instance().createCopyFromAvatar( + LLWearable* new_wearable = LLWearableList::instance().createCopy( old_wearable, trunc_name); LLPointer cb = @@ -391,7 +511,7 @@ void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, type, index, new_wearable, - addWearableToAgentInventoryCallback::CALL_UPDATE); + addWearableToAgentInventoryCallback::CALL_WEARITEM); LLUUID category_id; if (save_in_lost_and_found) { @@ -412,72 +532,28 @@ void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, new_name, cb); -/* - LLWearable* old_wearable = getWearable( type ); - if( old_wearable ) - { - std::string old_name = old_wearable->getName(); - old_wearable->setName( new_name ); - LLWearable* new_wearable = LLWearableList::instance().createCopyFromAvatar( old_wearable ); - old_wearable->setName( old_name ); - - LLUUID category_id; - LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID ); - if( item ) - { - new_wearable->setPermissions(item->getPermissions()); - if (save_in_lost_and_found) - { - category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); - } - else - { - // put in same folder as original - category_id = item->getParentUUID(); - } - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if(view) - { - view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); - } - } - - mWearableEntry[ type ].mWearable = new_wearable; - LLPointer cb = - new addWearableToAgentInventoryCallback( - LLPointer(NULL), - type, - addWearableToAgentInventoryCallback::CALL_UPDATE); - addWearableToAgentInventory(cb, new_wearable, category_id); - } -*/ + // old_wearable may still be referred to by other inventory items. Revert + // unsaved changes so other inventory items aren't affected by the changes + // that were just saved. + old_wearable->revertValues(); } -void LLAgentWearables::revertWearable(const LLWearableType::EType type, const U32 index) +void LLAgentWearables::revertWearable(const LLWearableType::EType type, const U32 index, bool set_by_user) { - llassert_always(index == 0); + //llassert_always(index == 0); LLWearable* wearable = getWearable(type, index); llassert(wearable); - if( wearable ) + if (wearable) { - wearable->writeToAvatar( TRUE ); + wearable->revertValues(); } gAgent.sendAgentSetAppearance(); } -void LLAgentWearables::revertAllWearables() -{ - for( S32 i=0; i < LLWearableType::WT_COUNT; i++ ) - { - for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) - revertWearable( (LLWearableType::EType)i, j); - } -} - void LLAgentWearables::saveAllWearables() { - //if(!gInventory.isLoaded()) + //if (!gInventory.isLoaded()) //{ // return; //} @@ -526,7 +602,7 @@ void LLAgentWearables::setWearableName( const LLUUID& item_id, const std::string BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const { - llassert_always(index == 0); + //llassert_always(index == 0); LLUUID item_id = getWearableItemID(type, index); return item_id.notNull() ? isWearableModifiable(item_id) : FALSE; } @@ -548,7 +624,7 @@ BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const BOOL LLAgentWearables::isWearableCopyable(LLWearableType::EType type, U32 index) const { - llassert_always(index == 0); + //llassert_always(index == 0); LLUUID item_id = getWearableItemID(type, index); if (!item_id.isNull()) { @@ -562,16 +638,9 @@ BOOL LLAgentWearables::isWearableCopyable(LLWearableType::EType type, U32 index) return FALSE; } -BOOL LLAgentWearables::areWearablesLoaded() const -{ - static const LLCachedControl rener_unloaded_avatar("RenderUnloadedAvatar"); - if(rener_unloaded_avatar) - return TRUE; - return mWearablesLoaded; -} - -/*U32 LLAgentWearables::getWearablePermMask(LLWearableType::EType type) const -{ +/* + U32 LLAgentWearables::getWearablePermMask(LLWearableType::EType type) + { LLUUID item_id = getWearableItemID(type); if(!item_id.isNull()) { @@ -587,7 +656,7 @@ BOOL LLAgentWearables::areWearablesLoaded() const LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::EType type, U32 index) { - llassert_always(index == 0); + //llassert_always(index == 0); LLUUID item_id = getWearableItemID(type,index); LLInventoryItem* item = NULL; if(item_id.notNull()) @@ -665,7 +734,7 @@ BOOL LLAgentWearables::selfHasWearable(LLWearableType::EType type) LLWearable* LLAgentWearables::getWearable(const LLWearableType::EType type, U32 index) { - llassert_always(index == 0); + //llassert_always(index == 0); wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type); if (wearable_iter == mWearableDatas.end()) { @@ -684,7 +753,7 @@ LLWearable* LLAgentWearables::getWearable(const LLWearableType::EType type, U32 void LLAgentWearables::setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable) { - llassert_always(index == 0); + //llassert_always(index == 0); LLWearable *old_wearable = getWearable(type,index); if (!old_wearable) { @@ -708,6 +777,7 @@ void LLAgentWearables::setWearable(const LLWearableType::EType type, U32 index, wearable_vec[index] = wearable; old_wearable->setLabelUpdated(); wearableUpdated(wearable); + checkWearableAgainstInventory(wearable); } } @@ -723,6 +793,7 @@ U32 LLAgentWearables::pushWearable(const LLWearableType::EType type, LLWearable { mWearableDatas[type].push_back(wearable); wearableUpdated(wearable); + checkWearableAgainstInventory(wearable); return mWearableDatas[type].size()-1; } return MAX_CLOTHING_PER_TYPE; @@ -733,6 +804,22 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable) gAgentAvatarp->wearableUpdated(wearable->getType(), FALSE); wearable->refreshName(); wearable->setLabelUpdated(); + + wearable->pullCrossWearableValues(); + + // Hack pt 2. If the wearable we just loaded has definition version 24, + // then force a re-save of this wearable after slamming the version number to 22. + // This number was incorrectly incremented for internal builds before release, and + // this fix will ensure that the affected wearables are re-saved with the right version number. + // the versions themselves are compatible. This code can be removed before release. + if( wearable->getDefinitionVersion() == 24 ) + { + wearable->setDefinitionVersion(22); + U32 index = getWearableIndex(wearable); + llinfos << "forcing werable type " << wearable->getType() << " to version 22 from 24" << llendl; + saveWearable(wearable->getType(),index,TRUE); + } + } void LLAgentWearables::popWearable(LLWearable *wearable) @@ -754,7 +841,7 @@ void LLAgentWearables::popWearable(LLWearable *wearable) void LLAgentWearables::popWearable(const LLWearableType::EType type, U32 index) { - llassert_always(index == 0); + //llassert_always(index == 0); LLWearable *wearable = getWearable(type, index); if (wearable) { @@ -792,7 +879,7 @@ U32 LLAgentWearables::getWearableIndex(const LLWearable *wearable) const const LLWearable* LLAgentWearables::getWearable(const LLWearableType::EType type, U32 index) const { - llassert_always(index == 0); + //llassert_always(index == 0); wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); if (wearable_iter == mWearableDatas.end()) { @@ -848,9 +935,19 @@ U32 LLAgentWearables::getWearableCount(const U32 tex_index) const } +BOOL LLAgentWearables::itemUpdatePending(const LLUUID& item_id) const +{ + return mItemsAwaitingWearableUpdate.find(item_id) != mItemsAwaitingWearableUpdate.end(); +} + +U32 LLAgentWearables::itemUpdatePendingCount() const +{ + return mItemsAwaitingWearableUpdate.size(); +} + const LLUUID LLAgentWearables::getWearableItemID(LLWearableType::EType type, U32 index) const { - llassert_always(index == 0); + //llassert_always(index == 0); const LLWearable *wearable = getWearable(type,index); if (wearable) return wearable->getItemID(); @@ -860,7 +957,7 @@ const LLUUID LLAgentWearables::getWearableItemID(LLWearableType::EType type, U32 const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U32 index) const { - llassert_always(index == 0); + //llassert_always(index == 0); const LLWearable *wearable = getWearable(type,index); if (wearable) return wearable->getAssetID(); @@ -868,10 +965,9 @@ const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U3 return LLUUID(); } -BOOL LLAgentWearables::isWearingItem( const LLUUID& item_id ) const +BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const { - const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - return (getWearableFromItemID(base_item_id) != NULL); + return getWearableFromItemID(item_id) != NULL; } // MULTI-WEARABLE: DEPRECATED (see backwards compatibility) @@ -885,10 +981,21 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs // that may result from AgentWearablesRequest having been sent more than once. if (mInitialWearablesUpdateReceived) return; + + if (isAgentAvatarValid()) + { + gAgentAvatarp->outputRezTiming("Received initial wearables update"); + } + + // notify subscribers that wearables started loading. See EXT-7777 + // *TODO: find more proper place to not be called from deprecated method. + // Seems such place is found: LLInitialWearablesFetch::processContents() + gAgentWearables.notifyLoadingStarted(); + mInitialWearablesUpdateReceived = true; LLUUID agent_id; - gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); + gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if (isAgentAvatarValid() && (agent_id == gAgentAvatarp->getID())) { @@ -899,19 +1006,27 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs if (num_wearables < NUM_BODY_PARTS) { // Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin). - // The fact that they don't have any here (only a dummy is sent) implies that this account existed - // before we had wearables, or that the database has gotten messed up. + // The fact that they don't have any here (only a dummy is sent) implies that either: + // 1. This account existed before we had wearables + // 2. The database has gotten messed up + // 3. This is the account's first login (i.e. the wearables haven't been generated yet). return; } + // Get the UUID of the current outfit folder (will be created if it doesn't exist) + const LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + LLInitialWearablesFetch* outfit = new LLInitialWearablesFetch(current_outfit_id); + //lldebugs << "processAgentInitialWearablesUpdate()" << llendl; // Add wearables - std::pair asset_id_array[ LLWearableType::WT_COUNT ]; + // MULTI-WEARABLE: DEPRECATED: Message only supports one wearable per type, will be ignored in future. + gAgentWearables.mItemsAwaitingWearableUpdate.clear(); for (S32 i=0; i < num_wearables; i++) { + // Parse initial wearables data from message system U8 type_u8 = 0; - gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i ); - if( type_u8 >= LLWearableType::WT_COUNT ) + gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i); + if (type_u8 >= LLWearableType::WT_COUNT) { continue; } @@ -929,104 +1044,36 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs else { LLAssetType::EType asset_type = LLWearableType::getAssetType( type ); - if( asset_type == LLAssetType::AT_NONE ) + if (asset_type == LLAssetType::AT_NONE) { continue; } - - asset_id_array[type] = std::pair(asset_id,item_id); + + // MULTI-WEARABLE: DEPRECATED: this message only supports one wearable per type. Should be ignored in future versions + + // Store initial wearables data until we know whether we have the current outfit folder or need to use the data. + LLInitialWearablesFetch::InitialWearableData wearable_data(type, item_id, asset_id); + outfit->add(wearable_data); } - - LL_DEBUGS("Wearables") << " " << LLWearableType::getTypeLabel(type) << " " << asset_id << " item id " << gAgentWearables.getWearableItemID(type, 0).asString() << LL_ENDL; + + lldebugs << " " << LLWearableType::getTypeLabel(type) << llendl; } - - LLCOFMgr::instance().fetchCOF(); - - // now that we have the asset ids...request the wearable assets - for(S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) + + // Get the complete information on the items in the inventory and set up an observer + // that will trigger when the complete information is fetched. + outfit->startFetch(); + if(outfit->isFinished()) { - LL_DEBUGS("Wearables") << " fetching " << asset_id_array[i].first << LL_ENDL; - const LLUUID item_id = asset_id_array[i].second; - if( !item_id.isNull() ) - { - LLWearableList::instance().getAsset( - asset_id_array[i].first, - LLStringUtil::null, - LLWearableType::getAssetType( (LLWearableType::EType) i ), - LLAgentWearables::onInitialWearableAssetArrived, - //This scary cast is to prevent messing with llwearablelist. Since ItemIDs are now tied to wearables, - // we now need to pass the ids to onInitialWearableAssetArrived so LLWearable::setItemID can be called there. - (void*)(intptr_t)new std::pair((LLWearableType::EType)i,item_id) ); - } + // everything is already here - call done. + outfit->done(); } - - // Not really sure where else to put this - gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle, NULL); - } -} - -// A single wearable that the avatar was wearing on start-up has arrived from the database. -// static -void LLAgentWearables::onInitialWearableAssetArrived( LLWearable* wearable, void* userdata ) -{ - std::pair* wearable_data = (std::pair*)(intptr_t) userdata; - LLWearableType::EType type = wearable_data->first; - LLUUID item_id = wearable_data->second; - delete wearable_data; - - LLVOAvatar* avatar = gAgentAvatarp; - if( !avatar ) - { - return; - } - - if( wearable ) - { - llassert( type == wearable->getType() ); - wearable->setItemID(item_id); - gAgentWearables.setWearable(type,0,wearable); - - // disable composites if initial textures are baked - avatar->setupComposites(); - gAgentWearables.queryWearableCache(); - - wearable->writeToAvatar( FALSE ); - avatar->setCompositeUpdatesEnabled(TRUE); - gInventory.addChangedMask( LLInventoryObserver::LABEL, item_id ); - } - else - { - // Somehow the asset doesn't exist in the database. - gAgentWearables.recoverMissingWearable( type, 0 ); - } - - gInventory.notifyObservers(); - - // Have all the wearables that the avatar was wearing at log-in arrived? - if( !gAgentWearables.mWearablesLoaded ) - { - gAgentWearables.mWearablesLoaded = TRUE; - for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) + else { - if( !gAgentWearables.getWearableItemID((LLWearableType::EType)i, 0).isNull() && !gAgentWearables.getWearable((LLWearableType::EType)i, 0) ) - { - gAgentWearables.mWearablesLoaded = FALSE; - break; - } - } - } - - if( gAgentWearables.mWearablesLoaded ) - { - // Make sure that the server's idea of the avatar's wearables actually match the wearables. - gAgent.sendAgentSetAppearance(); - - // Check to see if there are any baked textures that we hadn't uploaded before we logged off last time. - // If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive. - if( !gAgentCamera.cameraCustomizeAvatar() ) - { - avatar->requestLayerSetUploads(); + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(outfit); } + } } @@ -1035,14 +1082,14 @@ void LLAgentWearables::onInitialWearableAssetArrived( LLWearable* wearable, void // the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.) void LLAgentWearables::recoverMissingWearable(const LLWearableType::EType type, U32 index) { - llassert_always(index == 0); + //llassert_always(index == 0); // Try to recover by replacing missing wearable with a new one. LLNotificationsUtil::add("ReplacedMissingWearable"); lldebugs << "Wearable " << LLWearableType::getTypeLabel( type ) << " could not be downloaded. Replaced inventory item with default wearable." << llendl; LLWearable* new_wearable = LLWearableList::instance().createNewWearable(type); setWearable(type,index,new_wearable); - new_wearable->writeToAvatar( TRUE ); + //new_wearable->writeToAvatar( TRUE ); // Add a new one in the lost and found folder. // (We used to overwrite the "not found" one, but that could potentially @@ -1074,19 +1121,102 @@ void LLAgentWearables::recoverMissingWearableDone() } } -void LLAgentWearables::createStandardWearables(BOOL female) +void LLAgentWearables::addLocalTextureObject(const LLWearableType::EType wearable_type, const LLVOAvatarDefines::ETextureIndex texture_type, U32 wearable_index) { - llwarns << "Creating Standard " << (female ? "female" : "male" ) - << " Wearables" << llendl; + LLWearable* wearable = getWearable((LLWearableType::EType)wearable_type, wearable_index); + if (!wearable) + { + llerrs << "Tried to add local texture object to invalid wearable with type " << wearable_type << " and index " << wearable_index << llendl; + return; + } + LLLocalTextureObject lto; + wearable->setLocalTextureObject(texture_type, lto); +} + +class OnWearableItemCreatedCB: public LLInventoryCallback +{ +public: + OnWearableItemCreatedCB(): + mWearablesAwaitingItems(LLWearableType::WT_COUNT,NULL) + { + llinfos << "created callback" << llendl; + } + /* virtual */ void fire(const LLUUID& inv_item) + { + llinfos << "One item created " << inv_item.asString() << llendl; + LLViewerInventoryItem *item = gInventory.getItem(inv_item); + mItemsToLink.put(item); + updatePendingWearable(inv_item); + } + ~OnWearableItemCreatedCB() + { + llinfos << "All items created" << llendl; + LLPointer link_waiter = new LLUpdateAppearanceOnDestroy; + LLAppearanceMgr::instance().linkAll(LLAppearanceMgr::instance().getCOF(), + mItemsToLink, + link_waiter); + } + void addPendingWearable(LLWearable *wearable) + { + if (!wearable) + { + llwarns << "no wearable" << llendl; + return; + } + LLWearableType::EType type = wearable->getType(); + if (typeisWearableType()) + { + llwarns << "non-wearable item found" << llendl; + return; + } + if (item && item->isWearableType()) + { + LLWearableType::EType type = item->getWearableType(); + if (type < LLWearableType::WT_COUNT) + { + LLWearable *wearable = mWearablesAwaitingItems[type]; + if (wearable) + wearable->setItemID(inv_item); + } + else + { + llwarns << "invalid wearable type " << type << llendl; + } + } + } + +private: + LLInventoryModel::item_array_t mItemsToLink; + std::vector mWearablesAwaitingItems; +}; + +void LLAgentWearables::createStandardWearables() +{ + llwarns << "Creating standard wearables" << llendl; if (!isAgentAvatarValid()) return; - gAgentAvatarp->setSex(female ? SEX_FEMALE : SEX_MALE); - const BOOL create[LLWearableType::WT_COUNT] = - { - TRUE, //WT_SHAPE - TRUE, //WT_SKIN + { + TRUE, //LLWearableType::WT_SHAPE + TRUE, //LLWearableType::WT_SKIN TRUE, //WT_HAIR TRUE, //WT_EYES TRUE, //WT_SHIRT @@ -1103,265 +1233,65 @@ void LLAgentWearables::createStandardWearables(BOOL female) FALSE, //WT_PHYSICS }; - for( S32 i=0; i < LLWearableType::WT_COUNT; i++ ) + LLPointer cb = new OnWearableItemCreatedCB; + for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { - bool once = false; - LLPointer donecb = NULL; - if( create[i] ) + if (create[i]) { - if (!once) - { - once = true; - donecb = new createStandardWearablesAllDoneCallback; - } llassert(getWearableCount((LLWearableType::EType)i) == 0); LLWearable* wearable = LLWearableList::instance().createNewWearable((LLWearableType::EType)i); + ((OnWearableItemCreatedCB*)(&(*cb)))->addPendingWearable(wearable); // no need to update here... - LLPointer cb = - new addWearableToAgentInventoryCallback( - donecb, - (LLWearableType::EType)i, - 0, - wearable, - addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE); - addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE); + LLUUID category_id = LLUUID::null; + create_inventory_item(gAgent.getID(), + gAgent.getSessionID(), + category_id, + wearable->getTransactionID(), + wearable->getName(), + wearable->getDescription(), + wearable->getAssetType(), + LLInventoryType::IT_WEARABLE, + wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + cb); } } } + void LLAgentWearables::createStandardWearablesDone(S32 type, U32 index) { - LLWearable* wearable = getWearable((LLWearableType::EType)type, index); + llinfos << "type " << type << " index " << index << llendl; - if (wearable) - { - wearable->writeToAvatar(TRUE); - } + if (!isAgentAvatarValid()) return; + gAgentAvatarp->updateVisualParams(); } void LLAgentWearables::createStandardWearablesAllDone() { // ... because sendAgentWearablesUpdate will notify inventory // observers. + llinfos << "all done?" << llendl; + mWearablesLoaded = TRUE; + checkWearablesLoaded(); + notifyLoadingFinished(); + updateServer(); // Treat this as the first texture entry message, if none received yet gAgentAvatarp->onFirstTEMessageReceived(); } -void LLAgentWearables::makeNewOutfit( - const std::string& new_folder_name, - const LLDynamicArray& wearables_to_include, - const LLDynamicArray& attachments_to_include, - BOOL rename_clothing) -{ - if (!gAgentAvatarp) - { - return; - } - - BOOL fUseLinks = !gSavedSettings.getBOOL("UseInventoryLinks") || - !gHippoGridManager->getConnectedGrid()->supportsInvLinks(); - BOOL fUseOutfits = gSavedSettings.getBOOL("UseOutfitFolders") && - gHippoGridManager->getConnectedGrid()->supportsInvLinks(); - - LLFolderType::EType typeDest = (fUseOutfits) ? LLFolderType::FT_MY_OUTFITS : LLFolderType::FT_CLOTHING; - LLFolderType::EType typeFolder = (fUseOutfits) ? LLFolderType::FT_OUTFIT : LLFolderType::FT_NONE; - - // First, make a folder for the outfit. - LLUUID folder_id = gInventory.createNewCategory(gInventory.findCategoryUUIDForType(typeDest), typeFolder, new_folder_name); - - bool found_first_item = false; - - /////////////////// - // Wearables - - if( wearables_to_include.count() ) - { - // Then, iterate though each of the wearables and save copies of them in the folder. - S32 i; - S32 count = wearables_to_include.count(); - LLPointer cbdone = NULL; - for( i = 0; i < count; ++i ) - { - S32 index = wearables_to_include[i]; - LLWearable* old_wearable = getWearable((LLWearableType::EType)index, 0); - if( old_wearable ) - { - LLViewerInventoryItem* item = gInventory.getItem(getWearableItemID((LLWearableType::EType)index, 0)); - llassert(item); - if (!item) - continue; - if (fUseOutfits) - { - std::string strOrdering = llformat("@%d", item->getWearableType() * 100); - - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), - folder_id, - item->getName(), - strOrdering, - LLAssetType::AT_LINK, - LLPointer(NULL)); - } - else - { - std::string new_name = item->getName(); - if (rename_clothing) - { - new_name = new_folder_name; - new_name.append(" "); - new_name.append(old_wearable->getTypeLabel()); - LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); - } - - if (fUseLinks || isWearableCopyable((LLWearableType::EType)index, 0)) - { - LLWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable); - if (rename_clothing) - { - new_wearable->setName(new_name); - } - - S32 todo = addWearableToAgentInventoryCallback::CALL_NONE; - if (!found_first_item) - { - found_first_item = true; - /* set the focus to the first item */ - todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE; - /* send the agent wearables update when done */ - cbdone = new sendAgentWearablesUpdateCallback; - } - LLPointer cb = - new addWearableToAgentInventoryCallback( - cbdone, - (LLWearableType::EType)index, - 0, - new_wearable, - todo); - if (isWearableCopyable((LLWearableType::EType)index, 0)) - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getLinkedUUID(), - folder_id, - new_name, - cb); - } - else - { - move_inventory_item( - gAgent.getID(), - gAgent.getSessionID(), - item->getLinkedUUID(), - folder_id, - new_name, - cb); - } - } - else - { - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), - folder_id, - item->getName(), // Apparently, links cannot have arbitrary names... - item->getDescription(), - LLAssetType::AT_LINK, - LLPointer(NULL)); - } - } - } - } - gInventory.notifyObservers(); - } - - - /////////////////// - // Attachments - - if( attachments_to_include.count() ) - { - for( S32 i = 0; i < attachments_to_include.count(); i++ ) - { - S32 attachment_pt = attachments_to_include[i]; - LLViewerJointAttachment* attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, attachment_pt, (LLViewerJointAttachment*)NULL ); - if(!attachment) continue; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - LLViewerObject *attached_object = (*attachment_iter); - if (!attached_object) continue; - const LLUUID& item_id = attached_object->getAttachmentItemID(); - if (item_id.isNull()) continue; - LLInventoryItem* item = gInventory.getItem(item_id); - if (!item) continue; - if (fUseOutfits) - { - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), - folder_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK, - LLPointer(NULL)); - } - else - { - if (fUseLinks || item->getPermissions().allowCopyBy(gAgent.getID())) - { - const LLUUID& old_folder_id = item->getParentUUID(); - - move_inventory_item( - gAgent.getID(), - gAgent.getSessionID(), - item->getLinkedUUID(), - folder_id, - item->getName(), - LLPointer(NULL)); - - if (item->getPermissions().allowCopyBy(gAgent.getID())) - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getLinkedUUID(), - old_folder_id, - item->getName(), - LLPointer(NULL)); - } - } - else - { - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), - folder_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK, - LLPointer(NULL)); - } - } - } - } - } -} - void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index) { LLUUID first_item_id = getWearableItemID((LLWearableType::EType)type, index); // Open the inventory and select the first item we added. - if( first_item_id.notNull() ) + if (first_item_id.notNull()) { - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if(view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO); + active_panel->setSelection(first_item_id, TAKE_FOCUS_NO); } } } @@ -1387,7 +1317,7 @@ void LLAgentWearables::addWearableToAgentInventory(LLPointer& wearables, - BOOL remove ) + BOOL remove) { - lldebugs << "setWearableOutfit() start" << llendl; + llinfos << "setWearableOutfit() start" << llendl; - BOOL wearables_to_remove[LLWearableType::WT_COUNT]; - wearables_to_remove[LLWearableType::WT_SHAPE] = FALSE; - wearables_to_remove[LLWearableType::WT_SKIN] = FALSE; - wearables_to_remove[LLWearableType::WT_HAIR] = FALSE; - wearables_to_remove[LLWearableType::WT_EYES] = FALSE; -// [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.1.3b) | Added: RLVa-0.2.2a - wearables_to_remove[LLWearableType::WT_SHIRT] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_SHIRT); - wearables_to_remove[LLWearableType::WT_PANTS] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_PANTS); - wearables_to_remove[LLWearableType::WT_SHOES] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_SHOES); - wearables_to_remove[LLWearableType::WT_SOCKS] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_SOCKS); - wearables_to_remove[LLWearableType::WT_JACKET] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_JACKET); - wearables_to_remove[LLWearableType::WT_GLOVES] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_GLOVES); - wearables_to_remove[LLWearableType::WT_UNDERSHIRT] = (!gAgent.isTeen()) && remove && gRlvWearableLocks.canRemove(LLWearableType::WT_UNDERSHIRT); - wearables_to_remove[LLWearableType::WT_UNDERPANTS] = (!gAgent.isTeen()) && remove && gRlvWearableLocks.canRemove(LLWearableType::WT_UNDERPANTS); - wearables_to_remove[LLWearableType::WT_SKIRT] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_SKIRT); - wearables_to_remove[LLWearableType::WT_ALPHA] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_ALPHA); - wearables_to_remove[LLWearableType::WT_TATTOO] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_TATTOO); - wearables_to_remove[LLWearableType::WT_PHYSICS] = remove && gRlvWearableLocks.canRemove(LLWearableType::WT_PHYSICS); -// [/RLVa:KB] + // TODO: Removed check for ensuring that teens don't remove undershirt and underwear. Handle later + if (remove) + { + // note: shirt is the first non-body part wearable item. Update if wearable order changes. + // This loop should remove all clothing, but not any body parts + for (S32 type = 0; type < (S32)LLWearableType::WT_COUNT; type++) + { + if (LLWearableType::getAssetType((LLWearableType::EType)type) == LLAssetType::AT_CLOTHING) + { + removeWearable((LLWearableType::EType)type, true, 0); + } + } + } S32 count = wearables.count(); - llassert( items.count() == count ); + llassert(items.count() == count); S32 i; - for( i = 0; i < count; i++ ) + for (i = 0; i < count; i++) { LLWearable* new_wearable = wearables[i]; LLPointer new_item = items[i]; llassert(new_wearable); - if (new_wearable) { - LLWearableType::EType type = new_wearable->getType(); - wearables_to_remove[type] = FALSE; + const LLWearableType::EType type = new_wearable->getType(); - LLWearable* old_wearable = getWearable(type, 0); - if( old_wearable ) - { - const LLUUID& old_item_id = getWearableItemID(type, 0); - if( (old_wearable->getAssetID() == new_wearable->getAssetID()) && - (old_item_id == new_item->getLinkedUUID()) ) - { - lldebugs << "No change to wearable asset and item: " << LLWearableType::getTypeName( type ) << llendl; - continue; - } - - gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); - - // Assumes existing wearables are not dirty. - if( old_wearable->isDirty() ) - { - llassert(0); - continue; - } - } - - if (isFirstPhysicsWearable(type, new_item, new_wearable)) + /*if (isFirstPhysicsWearable(type, new_item, new_wearable)) { return; - } + }*/ new_wearable->setName(new_item->getName()); - new_wearable->setItemID(new_item->getLinkedUUID()); + new_wearable->setItemID(new_item->getUUID()); if (LLWearableType::getAssetType(type) == LLAssetType::AT_BODYPART) { // exactly one wearable per body part setWearable(type,0,new_wearable); } - else if(old_wearable) //Remove when multi-wearables are implemented. - { - setWearable(type,0,new_wearable); - } else { pushWearable(type,new_wearable); } - } - } - - std::vector wearables_being_removed; - - for( i = 0; i < LLWearableType::WT_COUNT; i++ ) - { - if( wearables_to_remove[i] ) - { - LLWearable* wearable = getWearable((LLWearableType::EType)i, 0); - wearables_being_removed.push_back(getWearable((LLWearableType::EType)i, 0)); - popWearable(wearable); - - gInventory.addChangedMask(LLInventoryObserver::LABEL, wearable->getItemID()); + wearableUpdated(new_wearable); + checkWearableAgainstInventory(new_wearable); } } gInventory.notifyObservers(); - queryWearableCache(); - - std::vector::iterator wearable_iter; - - for( wearable_iter = wearables_being_removed.begin(); - wearable_iter != wearables_being_removed.end(); - ++wearable_iter) + if (isAgentAvatarValid()) { - LLWearable* wearablep = *wearable_iter; - if (wearablep) - { - wearablep->removeFromAvatar( TRUE ); - } - } - - for( i = 0; i < count; i++ ) - { - wearables[i]->writeToAvatar( TRUE ); + gAgentAvatarp->setCompositeUpdatesEnabled(TRUE); + gAgentAvatarp->updateVisualParams(); + gAgentAvatarp->invalidateAll(); } // Start rendering & update the server mWearablesLoaded = TRUE; + checkWearablesLoaded(); + notifyLoadingFinished(); + queryWearableCache(); updateServer(); + gAgentAvatarp->dumpAvatarTEs("setWearableOutfit"); lldebugs << "setWearableOutfit() end" << llendl; } // User has picked "wear on avatar" from a menu. -void LLAgentWearables::setWearableItem( LLInventoryItem* new_item, LLWearable* new_wearable ) +void LLAgentWearables::setWearableItem(LLInventoryItem* new_item, LLWearable* new_wearable, bool do_append) { //LLAgentDumper dumper("setWearableItem"); if (isWearingItem(new_item->getUUID())) @@ -1675,44 +1536,41 @@ void LLAgentWearables::setWearableItem( LLInventoryItem* new_item, LLWearable* n llwarns << "wearable " << new_item->getUUID() << " is already worn" << llendl; return; } - LLWearableType::EType type = new_wearable->getType(); + + const LLWearableType::EType type = new_wearable->getType(); - -// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.1.4a) - // Block if: we can't wear on that layer; or we're already wearing something there we can't take off - if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canWear(dynamic_cast(new_item))) ) + /*if (isFirstPhysicsWearable(type, new_item, new_wearable)) { return; - } -// [/RLVa:KB] - - if (isFirstPhysicsWearable(type, new_item, new_wearable)) + }*/ + + if (!do_append) { - return; - } - - LLWearable* old_wearable = getWearable(type,0); - if( old_wearable ) - { - const LLUUID& old_item_id = old_wearable->getItemID(); - if( (old_wearable->getAssetID() == new_wearable->getAssetID()) && - (old_item_id == new_item->getUUID()) ) + // Remove old wearable, if any + // MULTI_WEARABLE: hardwired to 0 + LLWearable* old_wearable = getWearable(type,0); + if( old_wearable ) { - lldebugs << "No change to wearable asset and item: " << LLWearableType::getTypeName( type ) << llendl; - return; - } + const LLUUID& old_item_id = old_wearable->getItemID(); + if( (old_wearable->getAssetID() == new_wearable->getAssetID()) && + (old_item_id == new_item->getUUID()) ) + { + lldebugs << "No change to wearable asset and item: " << LLWearableType::getTypeName( type ) << llendl; + return; + } - if( old_wearable->isDirty() ) - { - // Bring up modal dialog: Save changes? Yes, No, Cancel - LLSD payload; - payload["item_id"] = new_item->getUUID(); - LLNotificationsUtil::add( "WearableSave", LLSD(), payload, boost::bind(onSetWearableDialog, _1, _2, new_wearable)); - return; + if( old_wearable->isDirty() ) + { + // Bring up modal dialog: Save changes? Yes, No, Cancel + LLSD payload; + payload["item_id"] = new_item->getUUID(); + LLNotificationsUtil::add( "WearableSave", LLSD(), payload, boost::bind(onSetWearableDialog, _1, _2, new_wearable)); + return; + } } } - setWearableFinal( new_item, new_wearable ); + setWearableFinal(new_item, new_wearable, do_append); } // static @@ -1750,11 +1608,21 @@ bool LLAgentWearables::onSetWearableDialog( const LLSD& notification, const LLSD return false; } -// Called from setWearable() and onSetWearableDialog() to actually set the wearable. -void LLAgentWearables::setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable ) +// Called from setWearableItem() and onSetWearableDialog() to actually set the wearable. +// MULTI_WEARABLE: unify code after null objects are gone. +void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLWearable* new_wearable, bool do_append) { const LLWearableType::EType type = new_wearable->getType(); + if (do_append && getWearableItemID(type,0).notNull()) + { + new_wearable->setItemID(new_item->getUUID()); + mWearableDatas[type].push_back(new_wearable); + llinfos << "Added additional wearable for type " << type + << " size is now " << mWearableDatas[type].size() << llendl; + checkWearableAgainstInventory(new_wearable); + } + else { // Replace the old wearable with a new one. llassert(new_item->getAssetUUID() == new_wearable->getAssetID()); @@ -1765,7 +1633,7 @@ void LLAgentWearables::setWearableFinal( LLInventoryItem* new_item, LLWearable* { old_item_id = old_wearable->getItemID(); } - new_wearable->setItemID(new_item->getLinkedUUID()); + new_wearable->setItemID(new_item->getUUID()); setWearable(type,0,new_wearable); if (old_item_id.notNull()) @@ -1779,7 +1647,7 @@ void LLAgentWearables::setWearableFinal( LLInventoryItem* new_item, LLWearable* //llinfos << "LLVOAvatar::setWearableItem()" << llendl; queryWearableCache(); - new_wearable->writeToAvatar( TRUE ); + //new_wearable->writeToAvatar(TRUE); updateServer(); } @@ -1828,6 +1696,10 @@ void LLAgentWearables::queryWearableCache() //VWR-22113: gAgent.getRegion() can return null if invalid, seen here on logout if(gAgent.getRegion()) { + if (isAgentAvatarValid()) + { + gAgentAvatarp->outputRezTiming("Fetching textures from cache"); + } llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl; gMessageSystem->sendReliable(gAgent.getRegion()->getHost()); gAgentQueryManager.mNumPendingQueries++; @@ -1898,44 +1770,9 @@ void LLAgentWearables::userRemoveWearablesOfType(const LLWearableType::EType &ty } } -void LLAgentWearables::userRemoveAllClothes() -{ - // We have to do this up front to avoid having to deal with the case of multiple wearables being dirty. - if (gAgentCamera.cameraCustomizeAvatar()) - { - gFloaterCustomize->askToSaveIfDirty( LLAgentWearables::userRemoveAllClothesStep2, NULL ); - } - else - { - userRemoveAllClothesStep2( TRUE, NULL ); - } -} - -void LLAgentWearables::userRemoveAllClothesStep2( BOOL proceed, void* userdata ) -{ - if( proceed ) - { - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_SHIRT ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_PANTS ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_SHOES ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_SOCKS ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_JACKET ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_GLOVES ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_UNDERSHIRT ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_UNDERPANTS ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_SKIRT ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_ALPHA ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_TATTOO ); - gAgentWearables.userRemoveWearablesOfType( LLWearableType::WT_PHYSICS ); - } -} - // Combines userRemoveAllAttachments() and userAttachMultipleAttachments() logic to // get attachments into desired state with minimal number of adds/removes. -//void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array) -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2.0a) | Added: Catznip-2.2.0a -void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array, bool fAttachOnly) -// [/SL:KB] +void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array) { // Possible cases: // already wearing but not in request set -> take off. @@ -2002,7 +1839,7 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj // Remove everything in objects_to_remove // userRemoveMultipleAttachments(objects_to_remove); // [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2.0a) | Added: Catznip-2.2.0a - if (!fAttachOnly) + if (!gAgentAvatarp->isFullyLoaded()) { userRemoveMultipleAttachments(objects_to_remove); } @@ -2016,34 +1853,6 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo { if (!isAgentAvatarValid()) return; -// [RLVa:KB] - Checked: 2010-03-04 (RLVa-1.1.3b) | Modified: RLVa-1.2.0a - // RELEASE-RLVa: [SL-2.0.0] Check our callers and verify that erasing elements from the passed vector won't break random things - if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_REMOVE)) ) - { - llvo_vec_t::iterator itObj = objects_to_remove.begin(); - while (itObj != objects_to_remove.end()) - { - const LLViewerObject* pAttachObj = *itObj; - if (gRlvAttachmentLocks.isLockedAttachment(pAttachObj)) - { - itObj = objects_to_remove.erase(itObj); - - // Fall-back code: re-add the attachment if it got removed from COF somehow (compensates for possible bugs elsewhere) - LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items; - LLLinkedItemIDMatches f(pAttachObj->getAttachmentItemID()); - gInventory.collectDescendentsIf(LLCOFMgr::instance().getCOF(), folders, items, LLInventoryModel::EXCLUDE_TRASH, f); - RLV_ASSERT( 0 != items.count() ); - if (0 == items.count()) - LLCOFMgr::instance().addAttachment(pAttachObj->getAttachmentItemID()); - } - else - { - ++itObj; - } - } - } -// [/RLVa:KB] - if (objects_to_remove.empty()) return; @@ -2079,10 +1888,7 @@ void LLAgentWearables::userRemoveAllAttachments() ++attachment_iter) { LLViewerObject *attached_object = (*attachment_iter); -// if (attached_object) -// [RLVa:KB] - Checked: 2010-09-28 (RLVa-1.1.3b) | Modified: RLVa-1.1.3b - if ( (attached_object) && ((!rlv_handler_t::isEnabled()) || (!gRlvAttachmentLocks.isLockedAttachment(attached_object))) ) -// [/RLVa:KB] + if (attached_object) { objects_to_remove.push_back(attached_object); } @@ -2159,22 +1965,229 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra } } } + +void LLAgentWearables::checkWearablesLoaded() const +{ +#ifdef SHOW_ASSERT + U32 item_pend_count = itemUpdatePendingCount(); + if (mWearablesLoaded) + { + llassert(item_pend_count==0); + } +#endif +} + +// Returns false if the given wearable is already topmost/bottommost +// (depending on closer_to_body parameter). +bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body) +{ + const LLWearable* wearable = getWearableFromItemID(item_id); + if (!wearable) return false; + + LLWearableType::EType wtype = wearable->getType(); + const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype); + if (!marginal_wearable) return false; + + return wearable != marginal_wearable; +} + +BOOL LLAgentWearables::areWearablesLoaded() const +{ + checkWearablesLoaded(); + return mWearablesLoaded; +} + // MULTI-WEARABLE: DEPRECATED: item pending count relies on old messages that don't support multi-wearables. do not trust to be accurate void LLAgentWearables::updateWearablesLoaded() { - mWearablesLoaded = TRUE; - for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) + mWearablesLoaded = (itemUpdatePendingCount()==0); + if (mWearablesLoaded) { - if( !getWearableItemID((LLWearableType::EType)i, 0).isNull() && !getWearable((LLWearableType::EType)i, 0) ) + notifyLoadingFinished(); + } +} + +bool LLAgentWearables::canWearableBeRemoved(const LLWearable* wearable) const +{ + if (!wearable) return false; + + LLWearableType::EType type = wearable->getType(); + // Make sure the user always has at least one shape, skin, eyes, and hair type currently worn. + return !(((type == LLWearableType::WT_SHAPE) || (type == LLWearableType::WT_SKIN) || (type == LLWearableType::WT_HAIR) || (type == LLWearableType::WT_EYES)) + && (getWearableCount(type) <= 1) ); +} +void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake) +{ + for( S32 type = 0; type < LLWearableType::WT_COUNT; ++type ) + { + for (S32 count = 0; count < (S32)getWearableCount((LLWearableType::EType)type); ++count) { - mWearablesLoaded = FALSE; - break; + LLWearable *wearable = getWearable((LLWearableType::EType)type,count); + llassert(wearable); + if (wearable) + { + wearable->animateParams(delta, upload_bake); + } } } - } + +bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool closer_to_body) +{ + if (!item) return false; + if (!item->isWearableType()) return false; + + wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(item->getWearableType()); + if (wearable_iter == mWearableDatas.end()) return false; + + wearableentry_vec_t& wearable_vec = wearable_iter->second; + if (wearable_vec.empty()) return false; + + const LLUUID& asset_id = item->getAssetUUID(); + + //nowhere to move if the wearable is already on any boundary (closest to the body/furthest from the body) + if (closer_to_body && asset_id == wearable_vec.front()->getAssetID()) return false; + if (!closer_to_body && asset_id == wearable_vec.back()->getAssetID()) return false; + + for (U32 i = 0; i < wearable_vec.size(); ++i) + { + LLWearable* wearable = wearable_vec[i]; + if (!wearable) continue; + if (wearable->getAssetID() != asset_id) continue; + + //swapping wearables + U32 swap_i = closer_to_body ? i-1 : i+1; + wearable_vec[i] = wearable_vec[swap_i]; + wearable_vec[swap_i] = wearable; + return true; + } + + return false; +} + +// static +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) +{ + if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return; + + LLWearable* wearable = LLWearableList::instance().createNewWearable(type); + LLAssetType::EType asset_type = wearable->getAssetType(); + LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; + LLPointer cb = wear ? new LLWearAndEditCallback : NULL; + LLUUID folder_id; + + if (parent_id.notNull()) + { + folder_id = parent_id; + } + else + { + LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); + folder_id = gInventory.findCategoryUUIDForType(folder_type); + } + + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), + folder_id, wearable->getTransactionID(), wearable->getName(), + wearable->getDescription(), asset_type, inv_type, wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + cb); +} + +// static +void LLAgentWearables::editWearable(const LLUUID& item_id) +{ + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); + if (!item) + { + llwarns << "Failed to get linked item" << llendl; + return; + } + + LLWearable* wearable = gAgentWearables.getWearableFromItemID(item_id); + if (!wearable) + { + llwarns << "Cannot get wearable" << llendl; + return; + } + + if (!gAgentWearables.isWearableModifiable(item->getUUID())) + { + llwarns << "Cannot modify wearable" << llendl; + return; + } + + //const BOOL disable_camera_switch = LLWearableType::getDisableCameraSwitch(wearable->getType()); + + // Set the tab to the right wearable. + LLFloaterCustomize::setCurrentWearableType( wearable->getType() ); + + if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() ) + { + // Start Avatar Customization + gAgentCamera.changeCameraToCustomizeAvatar(); + } +} + +// Request editing the item after it gets worn. +void LLAgentWearables::requestEditingWearable(const LLUUID& item_id) +{ + mItemToEdit = gInventory.getLinkedItemID(item_id); +} + +// Start editing the item if previously requested. +void LLAgentWearables::editWearableIfRequested(const LLUUID& item_id) +{ + if (mItemToEdit.notNull() && + mItemToEdit == gInventory.getLinkedItemID(item_id)) + { + LLAgentWearables::editWearable(item_id); + mItemToEdit.setNull(); + } +} + void LLAgentWearables::updateServer() { sendAgentWearablesUpdate(); gAgent.sendAgentSetAppearance(); } + +void LLAgentWearables::populateMyOutfitsFolder(void) +{ + llinfos << "starting outfit population" << llendl; + + const LLUUID& my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + LLLibraryOutfitsFetch* outfits = new LLLibraryOutfitsFetch(my_outfits_id); + outfits->mMyOutfitsID = my_outfits_id; + + // Get the complete information on the items in the inventory and + // setup an observer that will wait for that to happen. + gInventory.addObserver(outfits); + outfits->startFetch(); + if (outfits->isFinished()) + { + outfits->done(); + } +} + +boost::signals2::connection LLAgentWearables::addLoadingStartedCallback(loading_started_callback_t cb) +{ + return mLoadingStartedSignal.connect(cb); +} + +boost::signals2::connection LLAgentWearables::addLoadedCallback(loaded_callback_t cb) +{ + return mLoadedSignal.connect(cb); +} + +void LLAgentWearables::notifyLoadingStarted() +{ + mCOFChangeInProgress = true; + mLoadingStartedSignal(); +} + +void LLAgentWearables::notifyLoadingFinished() +{ + mCOFChangeInProgress = false; + mLoadedSignal(); +} +// EOF diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 630d57525..c84daa4ec 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -2,33 +2,26 @@ * @file llagentwearables.h * @brief LLAgentWearables class header file * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2010, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * 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://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * 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://secondlife.com/developers/opensource/flossexception + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - * */ #ifndef LL_LLAGENTWEARABLES_H @@ -63,16 +56,16 @@ public: LLAgentWearables(); virtual ~LLAgentWearables(); void setAvatarObject(LLVOAvatarSelf *avatar); - void createStandardWearables(BOOL female); + void createStandardWearables(); void cleanup(); - //void dump(); + void dump(); // LLInitClass interface static void initClass(); protected: void createStandardWearablesDone(S32 type, U32 index/* = 0*/); void createStandardWearablesAllDone(); - + //-------------------------------------------------------------------- // Queries //-------------------------------------------------------------------- @@ -83,18 +76,16 @@ public: BOOL isWearableCopyable(LLWearableType::EType type, U32 index /*= 0*/) const; BOOL areWearablesLoaded() const; + bool isCOFChangeInProgress() const { return mCOFChangeInProgress; } void updateWearablesLoaded(); - //void checkWearablesLoaded() const; - //bool canMoveWearable(const LLUUID& item_id, bool closer_to_body); + void checkWearablesLoaded() const; + bool canMoveWearable(const LLUUID& item_id, bool closer_to_body); // Note: False for shape, skin, eyes, and hair, unless you have MORE than 1. - //bool canWearableBeRemoved(const LLWearable* wearable) const; + bool canWearableBeRemoved(const LLWearable* wearable) const; - //void animateAllWearableParams(F32 delta, BOOL upload_bake); + void animateAllWearableParams(F32 delta, BOOL upload_bake); - BOOL needsReplacement(LLWearableType::EType wearableType, S32 remove); - //U32 getWearablePermMask(LLWearableType::EType type) const; - //-------------------------------------------------------------------- // Accessors //-------------------------------------------------------------------- @@ -109,11 +100,11 @@ public: LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/); const LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const; LLWearable* getTopWearable(const LLWearableType::EType type); - LLWearable* getBottomWearable(const LLWearableType::EType type); + LLWearable* getBottomWearable(const LLWearableType::EType type); U32 getWearableCount(const LLWearableType::EType type) const; U32 getWearableCount(const U32 tex_index) const; - static const U32 MAX_CLOTHING_PER_TYPE = 1; + static const U32 MAX_CLOTHING_PER_TYPE = 5; //-------------------------------------------------------------------- @@ -129,31 +120,20 @@ private: void popWearable(const LLWearableType::EType type, U32 index); public: - void setWearableItem(LLInventoryItem* new_item, LLWearable* wearable); + void setWearableItem(LLInventoryItem* new_item, LLWearable* wearable, bool do_append = false); void setWearableOutfit(const LLInventoryItem::item_array_t& items, const LLDynamicArray< LLWearable* >& wearables, BOOL remove); void setWearableName(const LLUUID& item_id, const std::string& new_name); - //void addLocalTextureObject(const LLWearableType::EType wearable_type, const LLVOAvatarDefines::ETextureIndex texture_type, U32 wearable_index); + void addLocalTextureObject(const LLWearableType::EType wearable_type, const LLVOAvatarDefines::ETextureIndex texture_type, U32 wearable_index); U32 getWearableIndex(const LLWearable *wearable) const; protected: - void setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable ); + void setWearableFinal(LLInventoryItem* new_item, LLWearable* new_wearable, bool do_append = false); static bool onSetWearableDialog(const LLSD& notification, const LLSD& response, LLWearable* wearable); void addWearableToAgentInventory(LLPointer cb, LLWearable* wearable, const LLUUID& category_id = LLUUID::null, BOOL notify = TRUE); - - /** - * @brief Only public because of addWearableToAgentInventoryCallback. - * - * NOTE: Do not call this method unless you are the inventory callback. - * NOTE: This can suffer from race conditions when working on the - * same values for index. - * @param index The index in mWearableEntry. - * @param item_id The inventory item id of the new wearable to wear. - * @param wearable The actual wearable data. - */ void addWearabletoAgentInventoryDone(const LLWearableType::EType type, const U32 index, const LLUUID& item_id, @@ -166,15 +146,15 @@ protected: //-------------------------------------------------------------------- public: - //static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null); - //static void editWearable(const LLUUID& item_id); - //bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null); + static void editWearable(const LLUUID& item_id); + bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); - //void requestEditingWearable(const LLUUID& item_id); - //void editWearableIfRequested(const LLUUID& item_id); + void requestEditingWearable(const LLUUID& item_id); + void editWearableIfRequested(const LLUUID& item_id); private: - //LLUUID mItemToEdit; + LLUUID mItemToEdit; //-------------------------------------------------------------------- // Removing wearables @@ -185,7 +165,6 @@ private: void removeWearableFinal(const LLWearableType::EType type, bool do_remove_all /*= false*/, U32 index /*= 0*/); protected: static bool onRemoveWearableDialog(const LLSD& notification, const LLSD& response); - static void userRemoveAllClothesStep2(BOOL proceed, void* userdata ); // userdata is NULL //-------------------------------------------------------------------- // Server Communication @@ -201,7 +180,7 @@ protected: void sendAgentWearablesRequest(); void queryWearableCache(); void updateServer(); - static void onInitialWearableAssetArrived(LLWearable* wearable, void* userdata ); + static void onInitialWearableAssetArrived(LLWearable* wearable, void* userdata); //-------------------------------------------------------------------- // Outfits @@ -210,27 +189,20 @@ public: // Should only be called if we *know* we've never done so before, since users may // not want the Library outfits to stay in their quick outfit selector and can delete them. - //void populateMyOutfitsFolder(); - void makeNewOutfit( - const std::string& new_folder_name, - const LLDynamicArray& wearables_to_include, - const LLDynamicArray& attachments_to_include, - BOOL rename_clothing); -private: + void populateMyOutfitsFolder(); +private: void makeNewOutfitDone(S32 type, U32 index); //-------------------------------------------------------------------- // Save Wearables //-------------------------------------------------------------------- public: - void saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, BOOL save_in_lost_and_found ); + void saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, BOOL save_in_lost_and_found); void saveWearable(const LLWearableType::EType type, const U32 index, BOOL send_update = TRUE, const std::string new_name = ""); - void saveAllWearables(); - void revertWearable( LLWearableType::EType type, const U32 index); - void revertAllWearables(); + void revertWearable(const LLWearableType::EType type, const U32 index, bool set_by_user=false); //-------------------------------------------------------------------- // Static UI hooks @@ -238,15 +210,10 @@ public: public: static void userRemoveWearable(const LLWearableType::EType &type, const U32 &index); static void userRemoveWearablesOfType(const LLWearableType::EType &type); - static void userRemoveAllClothes(); - - typedef std::vector llvo_vec_t; -// static void userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2.0a) | Added: Catznip-2.2.0a - // Not the best way to go about this but other attempts changed far too much LL code to be a viable solution - static void userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array, bool fAttachOnly = false); -// [/SL:KB] + typedef std::vector llvo_vec_t; + + static void userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array); static void userRemoveMultipleAttachments(llvo_vec_t& llvo_array); static void userRemoveAllAttachments(); static void userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array); @@ -258,7 +225,7 @@ public: // Signals //-------------------------------------------------------------------- public: - /*typedef boost::function loading_started_callback_t; + typedef boost::function loading_started_callback_t; typedef boost::signals2::signal loading_started_signal_t; boost::signals2::connection addLoadingStartedCallback(loading_started_callback_t cb); @@ -267,9 +234,11 @@ public: boost::signals2::connection addLoadedCallback(loaded_callback_t cb); void notifyLoadingStarted(); - void notifyLoadingFinished();*/ + void notifyLoadingFinished(); private: + loading_started_signal_t mLoadingStartedSignal; // should be called before wearables are changed + loaded_signal_t mLoadedSignal; // emitted when all agent wearables get loaded //-------------------------------------------------------------------- // Member variables @@ -281,6 +250,12 @@ private: static BOOL mInitialWearablesUpdateReceived; BOOL mWearablesLoaded; + std::set mItemsAwaitingWearableUpdate; + + /** + * True if agent's outfit is being changed now. + */ + BOOL mCOFChangeInProgress; //-------------------------------------------------------------------------------- // Support classes @@ -300,31 +275,22 @@ private: class addWearableToAgentInventoryCallback : public LLInventoryCallback { public: - enum { + enum ETodo + { CALL_NONE = 0, CALL_UPDATE = 1, CALL_RECOVERDONE = 2, CALL_CREATESTANDARDDONE = 4, - CALL_MAKENEWOUTFITDONE = 8 - } EType; + CALL_MAKENEWOUTFITDONE = 8, + CALL_WEARITEM = 16 + }; - /** - * @brief Construct a callback for dealing with the wearables. - * - * Would like to pass the agent in here, but we can't safely - * count on it being around later. Just use gAgent directly. - * @param cb callback to execute on completion (??? unused ???) - * @param index Index for the wearable in the agent - * @param wearable The wearable data. - * @param todo Bitmask of actions to take on completion. - */ addWearableToAgentInventoryCallback(LLPointer cb, LLWearableType::EType type, U32 index, LLWearable* wearable, U32 todo = CALL_NONE); virtual void fire(const LLUUID& inv_item); - private: LLWearableType::EType mType; U32 mIndex; diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp new file mode 100644 index 000000000..8cba54347 --- /dev/null +++ b/indra/newview/llagentwearablesfetch.cpp @@ -0,0 +1,607 @@ +/** + * @file llagentwearablesfetch.cpp + * @brief LLAgentWearblesFetch class implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llagentwearablesfetch.h" + +#include "llagent.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llinventoryfunctions.h" +#include "llstartup.h" +#include "llvoavatarself.h" + + +class LLOrderMyOutfitsOnDestroy: public LLInventoryCallback +{ +public: + LLOrderMyOutfitsOnDestroy() {}; + + virtual ~LLOrderMyOutfitsOnDestroy() + { + if (!LLApp::isRunning()) + { + llwarns << "called during shutdown, skipping" << llendl; + return; + } + + const LLUUID& my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + if (my_outfits_id.isNull()) return; + + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(my_outfits_id, cats, items); + if (!cats) return; + + //My Outfits should at least contain saved initial outfit and one another outfit + if (cats->size() < 2) + { + llwarning("My Outfits category was not populated properly", 0); + return; + } + + llinfos << "Starting updating My Outfits with wearables ordering information" << llendl; + + for (LLInventoryModel::cat_array_t::iterator outfit_iter = cats->begin(); + outfit_iter != cats->end(); ++outfit_iter) + { + const LLUUID& cat_id = (*outfit_iter)->getUUID(); + if (cat_id.isNull()) continue; + + // saved initial outfit already contains wearables ordering information + if (cat_id == LLAppearanceMgr::getInstance()->getBaseOutfitUUID()) continue; + + LLAppearanceMgr::getInstance()->updateClothingOrderingInfo(cat_id); + } + + llinfos << "Finished updating My Outfits with wearables ordering information" << llendl; + } + + /* virtual */ void fire(const LLUUID& inv_item) {}; +}; + + +LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) : + LLInventoryFetchDescendentsObserver(cof_id) +{ + if (isAgentAvatarValid()) + { + gAgentAvatarp->outputRezTiming("Initial wearables fetch started"); + } +} + +LLInitialWearablesFetch::~LLInitialWearablesFetch() +{ +} + +// virtual +void LLInitialWearablesFetch::done() +{ + // Delay processing the actual results of this so it's not handled within + // gInventory.notifyObservers. The results will be handled in the next + // idle tick instead. + gInventory.removeObserver(this); + doOnIdleOneTime(boost::bind(&LLInitialWearablesFetch::processContents,this)); + if (isAgentAvatarValid()) + { + gAgentAvatarp->outputRezTiming("Initial wearables fetch done"); + } +} + +void LLInitialWearablesFetch::add(InitialWearableData &data) + +{ + mAgentInitialWearables.push_back(data); +} + +void LLInitialWearablesFetch::processContents() +{ + if(!gAgentAvatarp) //no need to process wearables if the agent avatar is deleted. + { + delete this; + return ; + } + + // Fetch the wearable items from the Current Outfit Folder + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + LLFindWearables is_wearable; + llassert_always(mComplete.size() != 0); + gInventory.collectDescendentsIf(mComplete.front(), cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH, is_wearable); + + LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); + if (wearable_array.count() > 0) + { + gAgentWearables.notifyLoadingStarted(); + LLAppearanceMgr::instance().updateAppearanceFromCOF(); + } + else + { + // if we're constructing the COF from the wearables message, we don't have a proper outfit link + LLAppearanceMgr::instance().setOutfitDirty(true); + processWearablesMessage(); + } + delete this; +} + +class LLFetchAndLinkObserver: public LLInventoryFetchItemsObserver +{ +public: + LLFetchAndLinkObserver(uuid_vec_t& ids): + LLInventoryFetchItemsObserver(ids) + { + } + ~LLFetchAndLinkObserver() + { + } + virtual void done() + { + gInventory.removeObserver(this); + + // Link to all fetched items in COF. + LLPointer link_waiter = new LLUpdateAppearanceOnDestroy; + for (uuid_vec_t::iterator it = mIDs.begin(); + it != mIDs.end(); + ++it) + { + LLUUID id = *it; + LLViewerInventoryItem *item = gInventory.getItem(*it); + if (!item) + { + llwarns << "fetch failed!" << llendl; + continue; + } + + link_inventory_item(gAgent.getID(), + item->getLinkedUUID(), + LLAppearanceMgr::instance().getCOF(), + item->getName(), + item->getDescription(), + LLAssetType::AT_LINK, + link_waiter); + } + } +}; + +void LLInitialWearablesFetch::processWearablesMessage() +{ + if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead. + { + const LLUUID current_outfit_id = LLAppearanceMgr::instance().getCOF(); + uuid_vec_t ids; + for (U8 i = 0; i < mAgentInitialWearables.size(); ++i) + { + // Populate the current outfit folder with links to the wearables passed in the message + InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback. + + if (wearable_data->mAssetID.notNull()) + { + ids.push_back(wearable_data->mItemID); + } + else + { + llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID " + << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl; + delete wearable_data; + } + } + + // Add all current attachments to the requested items as well. + if (isAgentAvatarValid()) + { + for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + if (!attachment) continue; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject* attached_object = (*attachment_iter); + if (!attached_object) continue; + const LLUUID& item_id = attached_object->getAttachmentItemID(); + if (item_id.isNull()) continue; + ids.push_back(item_id); + } + } + } + + // Need to fetch the inventory items for ids, then create links to them after they arrive. + LLFetchAndLinkObserver *fetcher = new LLFetchAndLinkObserver(ids); + fetcher->startFetch(); + // If no items to be fetched, done will never be triggered. + // TODO: Change LLInventoryFetchItemsObserver::fetchItems to trigger done() on this condition. + if (fetcher->isFinished()) + { + fetcher->done(); + } + else + { + gInventory.addObserver(fetcher); + } + } + else + { + LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL; + } +} + +LLLibraryOutfitsFetch::LLLibraryOutfitsFetch(const LLUUID& my_outfits_id) : + LLInventoryFetchDescendentsObserver(my_outfits_id), + mCurrFetchStep(LOFS_FOLDER), + mOutfitsPopulated(false) +{ + llinfos << "created" << llendl; + + mMyOutfitsID = LLUUID::null; + mClothingID = LLUUID::null; + mLibraryClothingID = LLUUID::null; + mImportedClothingID = LLUUID::null; + mImportedClothingName = "Imported Library Clothing"; +} + +LLLibraryOutfitsFetch::~LLLibraryOutfitsFetch() +{ + llinfos << "destroyed" << llendl; +} + +void LLLibraryOutfitsFetch::done() +{ + llinfos << "start" << llendl; + + // Delay this until idle() routine, since it's a heavy operation and + // we also can't have it run within notifyObservers. + doOnIdleOneTime(boost::bind(&LLLibraryOutfitsFetch::doneIdle,this)); + gInventory.removeObserver(this); // Prevent doOnIdleOneTime from being added twice. +} + +void LLLibraryOutfitsFetch::doneIdle() +{ + llinfos << "start" << llendl; + + gInventory.addObserver(this); // Add this back in since it was taken out during ::done() + + switch (mCurrFetchStep) + { + case LOFS_FOLDER: + folderDone(); + mCurrFetchStep = LOFS_OUTFITS; + break; + case LOFS_OUTFITS: + outfitsDone(); + mCurrFetchStep = LOFS_LIBRARY; + break; + case LOFS_LIBRARY: + libraryDone(); + mCurrFetchStep = LOFS_IMPORTED; + break; + case LOFS_IMPORTED: + importedFolderDone(); + mCurrFetchStep = LOFS_CONTENTS; + break; + case LOFS_CONTENTS: + contentsDone(); + break; + default: + llwarns << "Got invalid state for outfit fetch: " << mCurrFetchStep << llendl; + mOutfitsPopulated = TRUE; + break; + } + + // We're completely done. Cleanup. + if (mOutfitsPopulated) + { + gInventory.removeObserver(this); + delete this; + return; + } +} + +void LLLibraryOutfitsFetch::folderDone() +{ + llinfos << "start" << llendl; + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + // Early out if we already have items in My Outfits + // except the case when My Outfits contains just initial outfit + if (cat_array.count() > 1) + { + mOutfitsPopulated = true; + return; + } + + mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); + mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); + + // If Library->Clothing->Initial Outfits exists, use that. + LLNameCategoryCollector matchFolderFunctor("Initial Outfits"); + cat_array.clear(); + gInventory.collectDescendentsIf(mLibraryClothingID, + cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH, + matchFolderFunctor); + if (cat_array.count() > 0) + { + const LLViewerInventoryCategory *cat = cat_array.get(0); + mLibraryClothingID = cat->getUUID(); + } + + mComplete.clear(); + + // Get the complete information on the items in the inventory. + uuid_vec_t folders; + folders.push_back(mClothingID); + folders.push_back(mLibraryClothingID); + setFetchIDs(folders); + startFetch(); + if (isFinished()) + { + done(); + } +} + +void LLLibraryOutfitsFetch::outfitsDone() +{ + llinfos << "start" << llendl; + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + uuid_vec_t folders; + + // Collect the contents of the Library's Clothing folder + gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + llassert(cat_array.count() > 0); + for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin(); + iter != cat_array.end(); + ++iter) + { + const LLViewerInventoryCategory *cat = iter->get(); + + // Get the names and id's of every outfit in the library, skip "Ruth" + // because it's a low quality legacy outfit + if (cat->getName() != "Ruth") + { + // Get the name of every outfit in the library + folders.push_back(cat->getUUID()); + mLibraryClothingFolders.push_back(cat->getUUID()); + } + } + cat_array.clear(); + wearable_array.clear(); + + // Check if you already have an "Imported Library Clothing" folder + LLNameCategoryCollector matchFolderFunctor(mImportedClothingName); + gInventory.collectDescendentsIf(mClothingID, + cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH, + matchFolderFunctor); + if (cat_array.size() > 0) + { + const LLViewerInventoryCategory *cat = cat_array.get(0); + mImportedClothingID = cat->getUUID(); + } + + mComplete.clear(); + setFetchIDs(folders); + startFetch(); + if (isFinished()) + { + done(); + } +} + +class LLLibraryOutfitsCopyDone: public LLInventoryCallback +{ +public: + LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher): + mFireCount(0), mLibraryOutfitsFetcher(fetcher) + { + } + + virtual ~LLLibraryOutfitsCopyDone() + { + if (!LLApp::isExiting() && mLibraryOutfitsFetcher) + { + gInventory.addObserver(mLibraryOutfitsFetcher); + mLibraryOutfitsFetcher->done(); + } + } + + /* virtual */ void fire(const LLUUID& inv_item) + { + mFireCount++; + } +private: + U32 mFireCount; + LLLibraryOutfitsFetch * mLibraryOutfitsFetcher; +}; + +// Copy the clothing folders from the library into the imported clothing folder +void LLLibraryOutfitsFetch::libraryDone() +{ + llinfos << "start" << llendl; + + if (mImportedClothingID != LLUUID::null) + { + // Skip straight to fetching the contents of the imported folder + importedFolderFetch(); + return; + } + + // Remove observer; next autopopulation step will be triggered externally by LLLibraryOutfitsCopyDone. + gInventory.removeObserver(this); + + LLPointer copy_waiter = new LLLibraryOutfitsCopyDone(this); + mImportedClothingID = gInventory.createNewCategory(mClothingID, + LLFolderType::FT_NONE, + mImportedClothingName); + // Copy each folder from library into clothing unless it already exists. + for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin(); + iter != mLibraryClothingFolders.end(); + ++iter) + { + const LLUUID& src_folder_id = (*iter); // Library clothing folder ID + const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id); + if (!cat) + { + llwarns << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << llendl; + continue; + } + + if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id)) + { + llinfos << "Skipping non-outfit folder name:" << cat->getName() << llendl; + continue; + } + + // Don't copy the category if it already exists. + LLNameCategoryCollector matchFolderFunctor(cat->getName()); + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + gInventory.collectDescendentsIf(mImportedClothingID, + cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH, + matchFolderFunctor); + if (cat_array.size() > 0) + { + continue; + } + + LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID, + LLFolderType::FT_NONE, + cat->getName()); + LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter); + } +} + +void LLLibraryOutfitsFetch::importedFolderFetch() +{ + llinfos << "start" << llendl; + + // Fetch the contents of the Imported Clothing Folder + uuid_vec_t folders; + folders.push_back(mImportedClothingID); + + mComplete.clear(); + setFetchIDs(folders); + startFetch(); + if (isFinished()) + { + done(); + } +} + +void LLLibraryOutfitsFetch::importedFolderDone() +{ + llinfos << "start" << llendl; + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + uuid_vec_t folders; + + // Collect the contents of the Imported Clothing folder + gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin(); + iter != cat_array.end(); + ++iter) + { + const LLViewerInventoryCategory *cat = iter->get(); + + // Get the name of every imported outfit + folders.push_back(cat->getUUID()); + mImportedClothingFolders.push_back(cat->getUUID()); + } + + mComplete.clear(); + setFetchIDs(folders); + startFetch(); + if (isFinished()) + { + done(); + } +} + +void LLLibraryOutfitsFetch::contentsDone() +{ + llinfos << "start" << llendl; + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + + LLPointer order_myoutfits_on_destroy = new LLOrderMyOutfitsOnDestroy; + + for (uuid_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin(); + folder_iter != mImportedClothingFolders.end(); + ++folder_iter) + { + const LLUUID &folder_id = (*folder_iter); + const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (!cat) + { + llwarns << "Library folder import for uuid:" << folder_id << " failed to find folder." << llendl; + continue; + } + + //initial outfit should be already in My Outfits + if (cat->getName() == LLStartUp::getInitialOutfitName()) continue; + + // First, make a folder in the My Outfits directory. + LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName()); + + cat_array.clear(); + wearable_array.clear(); + // Collect the contents of each imported clothing folder, so we can create new outfit links for it + gInventory.collectDescendents(folder_id, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin(); + wearable_iter != wearable_array.end(); + ++wearable_iter) + { + const LLViewerInventoryItem *item = wearable_iter->get(); + link_inventory_item(gAgent.getID(), + item->getLinkedUUID(), + new_outfit_folder_id, + item->getName(), + item->getDescription(), + LLAssetType::AT_LINK, + order_myoutfits_on_destroy); + } + } + + mOutfitsPopulated = true; +} + diff --git a/indra/newview/llagentwearablesfetch.h b/indra/newview/llagentwearablesfetch.h new file mode 100644 index 000000000..bedc445c0 --- /dev/null +++ b/indra/newview/llagentwearablesfetch.h @@ -0,0 +1,114 @@ +/** + * @file llagentwearablesinitialfetch.h + * @brief LLAgentWearablesInitialFetch class header file + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLAGENTWEARABLESINITIALFETCH_H +#define LL_LLAGENTWEARABLESINITIALFETCH_H + +#include "llinventoryobserver.h" +#include "llwearabletype.h" +#include "lluuid.h" + +//-------------------------------------------------------------------- +// InitialWearablesFetch +// +// This grabs contents from the COF and processes them. +// The processing is handled in idle(), i.e. outside of done(), +// to avoid gInventory.notifyObservers recursion. +//-------------------------------------------------------------------- +class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver +{ + LOG_CLASS(LLInitialWearablesFetch); + +public: + LLInitialWearablesFetch(const LLUUID& cof_id); + ~LLInitialWearablesFetch(); + virtual void done(); + + struct InitialWearableData + { + LLWearableType::EType mType; + LLUUID mItemID; + LLUUID mAssetID; + InitialWearableData(LLWearableType::EType type, LLUUID& itemID, LLUUID& assetID) : + mType(type), + mItemID(itemID), + mAssetID(assetID) + {} + }; + + void add(InitialWearableData &data); + +protected: + void processWearablesMessage(); + void processContents(); + +private: + typedef std::vector initial_wearable_data_vec_t; + initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg +}; + +//-------------------------------------------------------------------- +// InitialWearablesFetch +// +// This grabs outfits from the Library and copies those over to the user's +// outfits folder, typically during first-ever login. +//-------------------------------------------------------------------- +class LLLibraryOutfitsFetch : public LLInventoryFetchDescendentsObserver +{ +public: + enum ELibraryOutfitFetchStep + { + LOFS_FOLDER = 0, + LOFS_OUTFITS, + LOFS_LIBRARY, + LOFS_IMPORTED, + LOFS_CONTENTS + }; + + LLLibraryOutfitsFetch(const LLUUID& my_outfits_id); + ~LLLibraryOutfitsFetch(); + + virtual void done(); + void doneIdle(); + LLUUID mMyOutfitsID; + void importedFolderFetch(); +protected: + void folderDone(); + void outfitsDone(); + void libraryDone(); + void importedFolderDone(); + void contentsDone(); + enum ELibraryOutfitFetchStep mCurrFetchStep; + uuid_vec_t mLibraryClothingFolders; + uuid_vec_t mImportedClothingFolders; + bool mOutfitsPopulated; + LLUUID mClothingID; + LLUUID mLibraryClothingID; + LLUUID mImportedClothingID; + std::string mImportedClothingName; +}; + +#endif // LL_AGENTWEARABLESINITIALFETCH_H diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp new file mode 100644 index 000000000..3db65ae72 --- /dev/null +++ b/indra/newview/llappearancemgr.cpp @@ -0,0 +1,3138 @@ +/** + * @file llappearancemgr.cpp + * @brief Manager for initiating appearance changes on the viewer + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llattachmentsmgr.h" +#include "llcommandhandler.h" +#include "lleventtimer.h" +#include "llgesturemgr.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llnotificationsutil.h" +#include "lloutfitobserver.h" +//#include "lloutfitslist.h" +#include "llselectmgr.h" +//#include "llsidepanelappearance.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llviewerregion.h" +#include "llwearablelist.h" +#include "rlvhandler.h" + +// RAII thingy to guarantee that a variable gets reset when the Setter +// goes out of scope. More general utility would be handy - TODO: +// check boost. +class BoolSetter +{ +public: + BoolSetter(bool& var): + mVar(var) + { + mVar = true; + } + ~BoolSetter() + { + mVar = false; + } +private: + bool& mVar; +}; + +char ORDER_NUMBER_SEPARATOR('@'); + +class LLOutfitUnLockTimer: public LLEventTimer +{ +public: + LLOutfitUnLockTimer(F32 period) : LLEventTimer(period) + { + // restart timer on BOF changed event + LLOutfitObserver::instance().addBOFChangedCallback(boost::bind( + &LLOutfitUnLockTimer::reset, this)); + stop(); + } + + /*virtual*/ + BOOL tick() + { + if(mEventTimer.hasExpired()) + { + LLAppearanceMgr::instance().setOutfitLocked(false); + } + return FALSE; + } + void stop() { mEventTimer.stop(); } + void start() { mEventTimer.start(); } + void reset() { mEventTimer.reset(); } + BOOL getStarted() { return mEventTimer.getStarted(); } + + LLTimer& getEventTimer() { return mEventTimer;} +}; + +// support for secondlife:///app/appearance SLapps +/*class LLAppearanceHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + // support secondlife:///app/appearance/show, but for now we just + // make all secondlife:///app/appearance SLapps behave this way + if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAppearance")) + { + LLNotificationsUtil::add("NoAppearance", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + } + + LLFloaterSidePanelContainer::showPanel("appearance", LLSD()); + return true; + } +}; + +LLAppearanceHandler gAppearanceHandler;*/ + + +LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLNameCategoryCollector has_name(name); + gInventory.collectDescendentsIf(parent_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + has_name); + if (0 == cat_array.count()) + return LLUUID(); + else + { + LLViewerInventoryCategory *cat = cat_array.get(0); + if (cat) + return cat->getUUID(); + else + { + llwarns << "null cat" << llendl; + return LLUUID(); + } + } +} + +class LLWearInventoryCategoryCallback : public LLInventoryCallback +{ +public: + LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append) + { + mCatID = cat_id; + mAppend = append; + } + void fire(const LLUUID& item_id) + { + /* + * Do nothing. We only care about the destructor + * + * The reason for this is that this callback is used in a hack where the + * same callback is given to dozens of items, and the destructor is called + * after the last item has fired the event and dereferenced it -- if all + * the events actually fire! + */ + } + +protected: + ~LLWearInventoryCategoryCallback() + { + llinfos << "done all inventory callbacks" << llendl; + + // Is the destructor called by ordinary dereference, or because the app's shutting down? + // If the inventory callback manager goes away, we're shutting down, no longer want the callback. + if( LLInventoryCallbackManager::is_instantiated() ) + { + LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend); + } + else + { + llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl; + } + } + +private: + LLUUID mCatID; + bool mAppend; +}; + + +//Inventory callback updating "dirty" state when destroyed +class LLUpdateDirtyState: public LLInventoryCallback +{ +public: + LLUpdateDirtyState() {} + virtual ~LLUpdateDirtyState() + { + if (LLAppearanceMgr::instanceExists()) + { + LLAppearanceMgr::getInstance()->updateIsDirty(); + } + } + virtual void fire(const LLUUID&) {} +}; + + +LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering): + mFireCount(0), + mUpdateBaseOrder(update_base_outfit_ordering) +{ +} + +LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() +{ + llinfos << "done update appearance on destroy" << llendl; + + if (!LLApp::isExiting()) + { + LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder); + } +} + +void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item) +{ + LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item); + const std::string item_name = item ? item->getName() : "ITEM NOT FOUND"; +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl; +#endif + mFireCount++; +} + +struct LLFoundData +{ + LLFoundData() : + mAssetType(LLAssetType::AT_NONE), + mWearableType(LLWearableType::WT_INVALID), + mWearable(NULL) {} + + LLFoundData(const LLUUID& item_id, + const LLUUID& asset_id, + const std::string& name, + const LLAssetType::EType& asset_type, + const LLWearableType::EType& wearable_type, + const bool is_replacement = false + ) : + mItemID(item_id), + mAssetID(asset_id), + mName(name), + mAssetType(asset_type), + mWearableType(wearable_type), + mIsReplacement(is_replacement), + mWearable( NULL ) {} + + LLUUID mItemID; + LLUUID mAssetID; + std::string mName; + LLAssetType::EType mAssetType; + LLWearableType::EType mWearableType; + LLWearable* mWearable; + bool mIsReplacement; +}; + + +class LLWearableHoldingPattern +{ + LOG_CLASS(LLWearableHoldingPattern); + +public: + LLWearableHoldingPattern(); + ~LLWearableHoldingPattern(); + + bool pollFetchCompletion(); + void onFetchCompletion(); + bool isFetchCompleted(); + bool isTimedOut(); + + void checkMissingWearables(); + bool pollMissingWearables(); + bool isMissingCompleted(); + void recoverMissingWearable(LLWearableType::EType type); + void clearCOFLinksForMissingWearables(); + + void onWearableAssetFetch(LLWearable *wearable); + void onAllComplete(); + + typedef std::list found_list_t; + found_list_t& getFoundList(); + void eraseTypeToLink(LLWearableType::EType type); + void eraseTypeToRecover(LLWearableType::EType type); + void setObjItems(const LLInventoryModel::item_array_t& items); + void setGestItems(const LLInventoryModel::item_array_t& items); + bool isMostRecent(); + void handleLateArrivals(); + void resetTime(F32 timeout); + +private: + found_list_t mFoundList; + LLInventoryModel::item_array_t mObjItems; + LLInventoryModel::item_array_t mGestItems; + typedef std::set type_set_t; + type_set_t mTypesToRecover; + type_set_t mTypesToLink; + S32 mResolved; + LLTimer mWaitTime; + bool mFired; + typedef std::set type_set_hp; + static type_set_hp sActiveHoldingPatterns; + bool mIsMostRecent; + std::set mLateArrivals; + bool mIsAllComplete; +}; + +LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns; + +LLWearableHoldingPattern::LLWearableHoldingPattern(): + mResolved(0), + mFired(false), + mIsMostRecent(true), + mIsAllComplete(false) +{ + if (sActiveHoldingPatterns.size()>0) + { + llinfos << "Creating LLWearableHoldingPattern when " + << sActiveHoldingPatterns.size() + << " other attempts are active." + << " Flagging others as invalid." + << llendl; + for (type_set_hp::iterator it = sActiveHoldingPatterns.begin(); + it != sActiveHoldingPatterns.end(); + ++it) + { + (*it)->mIsMostRecent = false; + } + + } + sActiveHoldingPatterns.insert(this); +} + +LLWearableHoldingPattern::~LLWearableHoldingPattern() +{ + sActiveHoldingPatterns.erase(this); +} + +bool LLWearableHoldingPattern::isMostRecent() +{ + return mIsMostRecent; +} + +LLWearableHoldingPattern::found_list_t& LLWearableHoldingPattern::getFoundList() +{ + return mFoundList; +} + +void LLWearableHoldingPattern::eraseTypeToLink(LLWearableType::EType type) +{ + mTypesToLink.erase(type); +} + +void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type) +{ + mTypesToRecover.erase(type); +} + +void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items) +{ + mObjItems = items; +} + +void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items) +{ + mGestItems = items; +} + +bool LLWearableHoldingPattern::isFetchCompleted() +{ + return (mResolved >= (S32)getFoundList().size()); // have everything we were waiting for? +} + +bool LLWearableHoldingPattern::isTimedOut() +{ + return mWaitTime.hasExpired(); +} + +void LLWearableHoldingPattern::checkMissingWearables() +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + std::vector found_by_type(LLWearableType::WT_COUNT,0); + std::vector requested_by_type(LLWearableType::WT_COUNT,0); + for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) + { + LLFoundData &data = *it; + if (data.mWearableType < LLWearableType::WT_COUNT) + requested_by_type[data.mWearableType]++; + if (data.mWearable) + found_by_type[data.mWearableType]++; + } + + for (S32 type = 0; type < LLWearableType::WT_COUNT; ++type) + { + if (requested_by_type[type] > found_by_type[type]) + { + llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl; + } + if (found_by_type[type] > 0) + continue; + if ( + // If at least one wearable of certain types (pants/shirt/skirt) + // was requested but none was found, create a default asset as a replacement. + // In all other cases, don't do anything. + // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud + // due to logic in LLVOAvatarSelf::getIsCloud(). + // For non-critical types (tatoo, socks, etc.) the wearable will just be missing. + (requested_by_type[type] > 0) && + ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT))) + { + mTypesToRecover.insert(type); + mTypesToLink.insert(type); + recoverMissingWearable((LLWearableType::EType)type); + llwarns << "need to replace " << type << llendl; + } + } + + resetTime(60.0F); + if (!pollMissingWearables()) + { + doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this)); + } +} + +void LLWearableHoldingPattern::onAllComplete() +{ + if (isAgentAvatarValid()) + { + gAgentAvatarp->outputRezTiming("Agent wearables fetch complete"); + } + + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + // Activate all gestures in this folder + if (mGestItems.count() > 0) + { + llinfos << "Activating " << mGestItems.count() << " gestures" << llendl; + + LLGestureMgr::instance().activateGestures(mGestItems); + + // Update the inventory item labels to reflect the fact + // they are active. + LLViewerInventoryCategory* catp = + gInventory.getCategory(LLAppearanceMgr::instance().getCOF()); + + if (catp) + { + gInventory.updateCategory(catp); + gInventory.notifyObservers(); + } + } + + // Update wearables. + llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl; + LLAppearanceMgr::instance().updateAgentWearables(this, false); + + // Update attachments to match those requested. + if (isAgentAvatarValid()) + { + llinfos << "Updating " << mObjItems.count() << " attachments" << llendl; + LLAgentWearables::userUpdateAttachments(mObjItems); + } + + if (isFetchCompleted() && isMissingCompleted()) + { + // Only safe to delete if all wearable callbacks and all missing wearables completed. + delete this; + } + else + { + mIsAllComplete = true; + handleLateArrivals(); + } +} + +void LLWearableHoldingPattern::onFetchCompletion() +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + checkMissingWearables(); +} + +// Runs as an idle callback until all wearables are fetched (or we time out). +bool LLWearableHoldingPattern::pollFetchCompletion() +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + bool completed = isFetchCompleted(); + bool timed_out = isTimedOut(); + bool done = completed || timed_out; + + if (done) + { + llinfos << "polling, done status: " << completed << " timed out " << timed_out + << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl; + + mFired = true; + + if (timed_out) + { + llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl; + } + + onFetchCompletion(); + } + return done; +} + +class RecoveredItemLinkCB: public LLInventoryCallback +{ +public: + RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder): + mHolder(holder), + mWearable(wearable), + mType(type) + { + } + void fire(const LLUUID& item_id) + { + if (!mHolder->isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + llinfos << "Recovered item link for type " << mType << llendl; + mHolder->eraseTypeToLink(mType); + // Add wearable to FoundData for actual wearing + LLViewerInventoryItem *item = gInventory.getItem(item_id); + LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; + + if (linked_item) + { + gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID()); + + if (item) + { + LLFoundData found(linked_item->getUUID(), + linked_item->getAssetUUID(), + linked_item->getName(), + linked_item->getType(), + linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID, + true // is replacement + ); + found.mWearable = mWearable; + mHolder->getFoundList().push_front(found); + } + else + { + llwarns << "inventory item not found for recovered wearable" << llendl; + } + } + else + { + llwarns << "inventory link not found for recovered wearable" << llendl; + } + } +private: + LLWearableHoldingPattern* mHolder; + LLWearable *mWearable; + LLWearableType::EType mType; +}; + +class RecoveredItemCB: public LLInventoryCallback +{ +public: + RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder): + mHolder(holder), + mWearable(wearable), + mType(type) + { + } + void fire(const LLUUID& item_id) + { + if (!mHolder->isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + llinfos << "Recovered item for type " << mType << llendl; + LLViewerInventoryItem *itemp = gInventory.getItem(item_id); + mWearable->setItemID(item_id); + LLPointer cb = new RecoveredItemLinkCB(mType,mWearable,mHolder); + mHolder->eraseTypeToRecover(mType); + llassert(itemp); + if (itemp) + { + link_inventory_item( gAgent.getID(), + item_id, + LLAppearanceMgr::instance().getCOF(), + itemp->getName(), + itemp->getDescription(), + LLAssetType::AT_LINK, + cb); + } + } +private: + LLWearableHoldingPattern* mHolder; + LLWearable *mWearable; + LLWearableType::EType mType; +}; + +void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type) +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + // Try to recover by replacing missing wearable with a new one. + LLNotificationsUtil::add("ReplacedMissingWearable"); + lldebugs << "Wearable " << LLWearableType::getTypeLabel(type) + << " could not be downloaded. Replaced inventory item with default wearable." << llendl; + LLWearable* wearable = LLWearableList::instance().createNewWearable(type); + + // Add a new one in the lost and found folder. + const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + LLPointer cb = new RecoveredItemCB(type,wearable,this); + + create_inventory_item(gAgent.getID(), + gAgent.getSessionID(), + lost_and_found_id, + wearable->getTransactionID(), + wearable->getName(), + wearable->getDescription(), + wearable->getAssetType(), + LLInventoryType::IT_WEARABLE, + wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + cb); +} + +bool LLWearableHoldingPattern::isMissingCompleted() +{ + return mTypesToLink.size()==0 && mTypesToRecover.size()==0; +} + +void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() +{ + for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) + { + LLFoundData &data = *it; + if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable)) + { + // Wearable link that was never resolved; remove links to it from COF + llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl; + LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false); + } + } +} + +bool LLWearableHoldingPattern::pollMissingWearables() +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + bool timed_out = isTimedOut(); + bool missing_completed = isMissingCompleted(); + bool done = timed_out || missing_completed; + + if (!done) + { + llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size() + << " links " << mTypesToLink.size() + << " wearables, timed out " << timed_out + << " elapsed " << mWaitTime.getElapsedTimeF32() + << " done " << done << llendl; + } + + if (done) + { + gAgentAvatarp->debugWearablesLoaded(); + + // BAP - if we don't call clearCOFLinksForMissingWearables() + // here, we won't have to add the link back in later if the + // wearable arrives late. This is to avoid corruption of + // wearable ordering info. Also has the effect of making + // unworn item links visible in the COF under some + // circumstances. + + //clearCOFLinksForMissingWearables(); + onAllComplete(); + } + return done; +} + +// Handle wearables that arrived after the timeout period expired. +void LLWearableHoldingPattern::handleLateArrivals() +{ + // Only safe to run if we have previously finished the missing + // wearables and other processing - otherwise we could be in some + // intermediate state - but have not been superceded by a later + // outfit change request. + if (mLateArrivals.size() == 0) + { + // Nothing to process. + return; + } + if (!isMostRecent()) + { + llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl; + } + if (!mIsAllComplete) + { + llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl; + } + + llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl; + + // Update mFoundList using late-arriving wearables. + std::set replaced_types; + for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + iter != getFoundList().end(); ++iter) + { + LLFoundData& data = *iter; + for (std::set::iterator wear_it = mLateArrivals.begin(); + wear_it != mLateArrivals.end(); + ++wear_it) + { + LLWearable *wearable = *wear_it; + + if(wearable->getAssetID() == data.mAssetID) + { + data.mWearable = wearable; + + replaced_types.insert(data.mWearableType); + + // BAP - if we didn't call + // clearCOFLinksForMissingWearables() earlier, we + // don't need to restore the link here. Fixes + // wearable ordering problems. + + // LLAppearanceMgr::instance().addCOFItemLink(data.mItemID,false); + + // BAP failing this means inventory or asset server + // are corrupted in a way we don't handle. + llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType)); + break; + } + } + } + + // Remove COF links for any default wearables previously used to replace the late arrivals. + // All this pussyfooting around with a while loop and explicit + // iterator incrementing is to allow removing items from the list + // without clobbering the iterator we're using to navigate. + LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + while (iter != getFoundList().end()) + { + LLFoundData& data = *iter; + + // If an item of this type has recently shown up, removed the corresponding replacement wearable from COF. + if (data.mWearable && data.mIsReplacement && + replaced_types.find(data.mWearableType) != replaced_types.end()) + { + LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false); + std::list::iterator clobber_ator = iter; + ++iter; + getFoundList().erase(clobber_ator); + } + else + { + ++iter; + } + } + + // Clear contents of late arrivals. + mLateArrivals.clear(); + + // Update appearance based on mFoundList + LLAppearanceMgr::instance().updateAgentWearables(this, false); +} + +void LLWearableHoldingPattern::resetTime(F32 timeout) +{ + mWaitTime.reset(); + mWaitTime.setTimerExpirySec(timeout); +} + +void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable) +{ + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + + mResolved += 1; // just counting callbacks, not successes. + llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl; + if (!wearable) + { + llwarns << "no wearable found" << llendl; + } + + if (mFired) + { + llwarns << "called after holder fired" << llendl; + if (wearable) + { + mLateArrivals.insert(wearable); + if (mIsAllComplete) + { + handleLateArrivals(); + } + } + return; + } + + if (!wearable) + { + return; + } + + for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + iter != getFoundList().end(); ++iter) + { + LLFoundData& data = *iter; + if(wearable->getAssetID() == data.mAssetID) + { + // Failing this means inventory or asset server are corrupted in a way we don't handle. + if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType)) + { + llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl; + break; + } + + data.mWearable = wearable; + } + } +} + +static void onWearableAssetFetch(LLWearable* wearable, void* data) +{ + LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data; + holder->onWearableAssetFetch(wearable); +} + + +static void removeDuplicateItems(LLInventoryModel::item_array_t& items) +{ + LLInventoryModel::item_array_t new_items; + std::set items_seen; + std::deque tmp_list; + // Traverse from the front and keep the first of each item + // encountered, so we actually keep the *last* of each duplicate + // item. This is needed to give the right priority when adding + // duplicate items to an existing outfit. + for (S32 i=items.count()-1; i>=0; i--) + { + LLViewerInventoryItem *item = items.get(i); + LLUUID item_id = item->getLinkedUUID(); + if (items_seen.find(item_id)!=items_seen.end()) + continue; + items_seen.insert(item_id); + tmp_list.push_front(item); + } + for (std::deque::iterator it = tmp_list.begin(); + it != tmp_list.end(); + ++it) + { + new_items.put(*it); + } + items = new_items; +} + +const LLUUID LLAppearanceMgr::getCOF() const +{ + return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +} + + +const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() +{ + const LLUUID& current_outfit_cat = getCOF(); + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + // Can't search on FT_OUTFIT since links to categories return FT_CATEGORY for type since they don't + // return preferred type. + LLIsType is_category( LLAssetType::AT_CATEGORY ); + gInventory.collectDescendentsIf(current_outfit_cat, + cat_array, + item_array, + false, + is_category, + false); + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + const LLViewerInventoryItem *item = (*iter); + const LLViewerInventoryCategory *cat = item->getLinkedCategory(); + if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) + { + const LLUUID parent_id = cat->getParentUUID(); + LLViewerInventoryCategory* parent_cat = gInventory.getCategory(parent_id); + // if base outfit moved to trash it means that we don't have base outfit + if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH) + { + return NULL; + } + return item; + } + } + return NULL; +} + +bool LLAppearanceMgr::getBaseOutfitName(std::string& name) +{ + const LLViewerInventoryItem* outfit_link = getBaseOutfitLink(); + if(outfit_link) + { + const LLViewerInventoryCategory *cat = outfit_link->getLinkedCategory(); + if (cat) + { + name = cat->getName(); + return true; + } + } + return false; +} + +const LLUUID LLAppearanceMgr::getBaseOutfitUUID() +{ + const LLViewerInventoryItem* outfit_link = getBaseOutfitLink(); + if (!outfit_link || !outfit_link->getIsLinkType()) return LLUUID::null; + + const LLViewerInventoryCategory* outfit_cat = outfit_link->getLinkedCategory(); + if (!outfit_cat) return LLUUID::null; + + if (outfit_cat->getPreferredType() != LLFolderType::FT_OUTFIT) + { + llwarns << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << llendl; + return LLUUID::null; + } + + return outfit_cat->getUUID(); +} + +bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer cb) +{ + if (item_id_to_wear.isNull()) return false; + + // *TODO: issue with multi-wearable should be fixed: + // in this case this method will be called N times - loading started for each item + // and than N times will be called - loading completed for each item. + // That means subscribers will be notified that loading is done after first item in a batch is worn. + // (loading indicator disappears for example before all selected items are worn) + // Have not fix this issue for 2.1 because of stability reason. EXT-7777. + + // Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times +// gAgentWearables.notifyLoadingStarted(); + + LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); + if (!item_to_wear) return false; + + if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) + { + LLPointer cb = new WearOnAvatarCallback(replace); + copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb); + return false; + } + else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) + { + return false; // not in library and not in agent's inventory + } + else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) + { + LLNotificationsUtil::add("CannotWearTrash"); + return false; + } + else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911 + { + return false; + } + + switch (item_to_wear->getType()) + { + case LLAssetType::AT_CLOTHING: + if (gAgentWearables.areWearablesLoaded()) + { + S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); + if ((replace && wearable_count != 0) || + (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) + { + removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false); + } + addCOFItemLink(item_to_wear, do_update, cb); + } + break; + case LLAssetType::AT_BODYPART: + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + // Remove the existing wearables of the same type. + // Remove existing body parts anyway because we must not be able to wear e.g. two skins. + removeCOFLinksOfType(item_to_wear->getWearableType(), false); + + addCOFItemLink(item_to_wear, do_update, cb); + break; + case LLAssetType::AT_OBJECT: + rez_attachment(item_to_wear, NULL, replace); + break; + default: return false;; + } + + return true; +} + +// Update appearance from outfit folder. +void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool append) +{ + if (!proceed) + return; + LLAppearanceMgr::instance().updateCOF(category,append); +} + +void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit); + wearInventoryCategory(cat, false, false); +} + +// Open outfit renaming dialog. +void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); + if (!cat) + { + return; + } + + LLSD args; + args["NAME"] = cat->getName(); + + LLSD payload; + payload["cat_id"] = outfit_id; + + LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2)); +} + +// User typed new outfit name. +// static +void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + std::string outfit_name = response["new_name"].asString(); + LLStringUtil::trim(outfit_name); + if (!outfit_name.empty()) + { + LLUUID cat_id = notification["payload"]["cat_id"].asUUID(); + rename_category(&gInventory, cat_id, outfit_name); + } +} + +void LLAppearanceMgr::setOutfitLocked(bool locked) +{ + if (mOutfitLocked == locked) + { + return; + } + + mOutfitLocked = locked; + if (locked) + { + mUnlockOutfitTimer->reset(); + mUnlockOutfitTimer->start(); + } + else + { + mUnlockOutfitTimer->stop(); + } + + LLOutfitObserver::instance().notifyOutfitLockChanged(); +} + +void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + wearInventoryCategory(cat, false, true); +} + +void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false); + + gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); + + LLInventoryModel::item_array_t::const_iterator it = items.begin(); + const LLInventoryModel::item_array_t::const_iterator it_end = items.end(); + for( ; it_end != it; ++it) + { + LLViewerInventoryItem* item = *it; + removeItemFromAvatar(item->getUUID()); + } +} + +// Create a copy of src_id + contents as a subfolder of dst_id. +void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer cb) +{ + LLInventoryCategory *src_cat = gInventory.getCategory(src_id); + if (!src_cat) + { + llwarns << "folder not found for src " << src_id.asString() << llendl; + return; + } + llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl; + LLUUID parent_id = dst_id; + if(parent_id.isNull()) + { + parent_id = gInventory.getRootFolderID(); + } + LLUUID subfolder_id = gInventory.createNewCategory( parent_id, + LLFolderType::FT_NONE, + src_cat->getName()); + shallowCopyCategoryContents(src_id, subfolder_id, cb); + + gInventory.notifyObservers(); +} + +// Copy contents of src_id to dst_id. +void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer cb) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(src_id, cats, items); + llinfos << "copying " << items->count() << " items" << llendl; + for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); + iter != items->end(); + ++iter) + { + const LLViewerInventoryItem* item = (*iter); + switch (item->getActualType()) + { + case LLAssetType::AT_LINK: + { + //LLInventoryItem::getDescription() is used for a new description + //to propagate ordering information saved in descriptions of links + link_inventory_item(gAgent.getID(), + item->getLinkedUUID(), + dst_id, + item->getName(), + item->LLInventoryItem::getDescription(), + LLAssetType::AT_LINK, cb); + break; + } + case LLAssetType::AT_LINK_FOLDER: + { + LLViewerInventoryCategory *catp = item->getLinkedCategory(); + // Skip copying outfit links. + if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT) + { + link_inventory_item(gAgent.getID(), + item->getLinkedUUID(), + dst_id, + item->getName(), + item->getDescription(), + LLAssetType::AT_LINK_FOLDER, cb); + } + break; + } + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_OBJECT: + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_GESTURE: + { + llinfos << "copying inventory item " << item->getName() << llendl; + copy_inventory_item(gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + dst_id, + item->getName(), + cb); + break; + } + default: + // Ignore non-outfit asset types + break; + } + } +} + +BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id) +{ + // These are the wearable items that are required for considering this + // folder as containing a complete outfit. + U32 required_wearables = 0; + required_wearables |= 1LL << LLWearableType::WT_SHAPE; + required_wearables |= 1LL << LLWearableType::WT_SKIN; + required_wearables |= 1LL << LLWearableType::WT_HAIR; + required_wearables |= 1LL << LLWearableType::WT_EYES; + + // These are the wearables that the folder actually contains. + U32 folder_wearables = 0; + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, cats, items); + for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); + iter != items->end(); + ++iter) + { + const LLViewerInventoryItem* item = (*iter); + if (item->isWearableType()) + { + const LLWearableType::EType wearable_type = item->getWearableType(); + folder_wearables |= 1LL << wearable_type; + } + } + + // If the folder contains the required wearables, return TRUE. + return ((required_wearables & folder_wearables) == required_wearables); +} + +bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) +{ + // Disallow removing the base outfit. + if (outfit_cat_id == getBaseOutfitUUID()) + { + return false; + } + + // Check if the outfit folder itself is removable. + if (!get_is_category_removable(&gInventory, outfit_cat_id)) + { + return false; + } + + // Check for the folder's non-removable descendants. + LLFindNonRemovableObjects filter_non_removable; + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryModel::item_array_t::const_iterator it; + gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable); + if (!cats.empty() || !items.empty()) + { + return false; + } + + return true; +} + +// static +bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(outfit_cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + is_worn); + return items.size() > 0; +} + +// static +bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id) +{ + if (gAgentWearables.isCOFChangeInProgress()) + { + return false; + } + + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(outfit_cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + return items.size() > 0; +} + +bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) +{ + // Don't allow wearing anything while we're changing appearance. + if (gAgentWearables.isCOFChangeInProgress()) + { + return false; + } + + // Check whether it's the base outfit. + if (outfit_cat_id.isNull() || outfit_cat_id == getBaseOutfitUUID()) + { + return false; + } + + // Check whether the outfit contains any wearables we aren't wearing already (STORM-702). + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true); + gInventory.collectDescendentsIf(outfit_cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + is_worn); + return items.size() > 0; +} + +void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(category, cats, items, + LLInventoryModel::EXCLUDE_TRASH); + for (S32 i = 0; i < items.count(); ++i) + { + LLViewerInventoryItem *item = items.get(i); + if (item->getActualType() != LLAssetType::AT_LINK_FOLDER) + continue; + if (item->getIsLinkType()) + { + LLViewerInventoryCategory* catp = item->getLinkedCategory(); + if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + gInventory.purgeObject(item->getUUID()); + } + } + } +} + +void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(category, cats, items, + LLInventoryModel::EXCLUDE_TRASH); + for (S32 i = 0; i < items.count(); ++i) + { + LLViewerInventoryItem *item = items.get(i); + if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER)) + continue; + if (item->getIsLinkType()) + { + gInventory.purgeObject(item->getUUID()); + } + } +} + +// Keep the last N wearables of each type. For viewer 2.0, N is 1 for +// both body parts and clothing items. +void LLAppearanceMgr::filterWearableItems( + LLInventoryModel::item_array_t& items, S32 max_per_type) +{ + // Divvy items into arrays by wearable type. + std::vector items_by_type(LLWearableType::WT_COUNT); + divvyWearablesByType(items, items_by_type); + + // rebuild items list, retaining the last max_per_type of each array + items.clear(); + for (S32 i=0; i cb) +{ + for (S32 i=0; igetLinkedUUID(), + cat_uuid, + item->getName(), + item->LLInventoryItem::getDescription(), + LLAssetType::AT_LINK, + cb); + + const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); + const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; +#endif + } +} + + +//void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0b) | Added: RLVa-1.2.0b +void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) +{ + LLInventoryModel::item_array_t body_items_new, wear_items_new, obj_items_new, gest_items_new; + getDescendentsOfAssetType(category, body_items_new, LLAssetType::AT_BODYPART, false); + getDescendentsOfAssetType(category, wear_items_new, LLAssetType::AT_CLOTHING, false); + getDescendentsOfAssetType(category, obj_items_new, LLAssetType::AT_OBJECT, false); + getDescendentsOfAssetType(category, gest_items_new, LLAssetType::AT_GESTURE, false); + updateCOF(body_items_new, wear_items_new, obj_items_new, gest_items_new, append, category); +} +void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, + LLInventoryModel::item_array_t& wear_items_new, + LLInventoryModel::item_array_t& obj_items_new, + LLInventoryModel::item_array_t& gest_items_new, + bool append /*=false*/, const LLUUID& idOutfit /*=LLUUID::null*/) +// [/RLVa:KB] +{ + LLViewerInventoryCategory *pcat = gInventory.getCategory(idOutfit); + llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl; + + const LLUUID cof = getCOF(); + + // Deactivate currently active gestures in the COF, if replacing outfit + if (!append) + { + LLInventoryModel::item_array_t gest_items; + getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); + for(S32 i = 0; i < gest_items.count(); ++i) + { + LLViewerInventoryItem *gest_item = gest_items.get(i); + if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) ) + { + LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() ); + } + } + } + + // Collect and filter descendents to determine new COF contents. + + // - Body parts: always include COF contents as a fallback in case any + // required parts are missing. + // Preserve body parts from COF if appending. + LLInventoryModel::item_array_t body_items; + getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false); +// getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false); +// [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b + // Filter out any new body parts that can't be worn before adding them + if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) + body_items_new.erase(std::remove_if(body_items_new.begin(), body_items_new.end(), RlvPredCanNotWearItem(RLV_WEAR_REPLACE)), body_items_new.end()); + body_items.insert(body_items.end(), body_items_new.begin(), body_items_new.end()); +// [/RLVa:KB] + // NOTE-RLVa: we don't actually want to favour COF body parts over the folder's body parts (if only because it breaks force wear) +// if (append) +// reverse(body_items.begin(), body_items.end()); + // Reduce body items to max of one per type. + removeDuplicateItems(body_items); + filterWearableItems(body_items, 1); + + // - Wearables: include COF contents only if appending. + LLInventoryModel::item_array_t wear_items; + if (append) + getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false); +// [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b + else if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) + { + // Make sure that all currently locked clothing layers remain in COF when replacing + getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false); + wear_items.erase(std::remove_if(wear_items.begin(), wear_items.end(), rlvPredCanRemoveItem), wear_items.end()); + } +// [/RLVa:KB] +// getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false); +// [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b + // Filter out any new wearables that can't be worn before adding them + if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) + wear_items_new.erase(std::remove_if(wear_items_new.begin(), wear_items_new.end(), RlvPredCanNotWearItem(RLV_WEAR)), wear_items_new.end()); + wear_items.insert(wear_items.end(), wear_items_new.begin(), wear_items_new.end()); +// [/RLVa:KB] + // Reduce wearables to max of one per type. + removeDuplicateItems(wear_items); +// [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e + //removeDuplicateWearableItemsByAssetID(wear_items); +// [/SL:KB] + filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); + + // - Attachments: include COF contents only if appending. + LLInventoryModel::item_array_t obj_items; + if (append) + getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false); +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b + else if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) + { + // Make sure that all currently locked attachments remain in COF when replacing + getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false); + obj_items.erase(std::remove_if(obj_items.begin(), obj_items.end(), rlvPredCanRemoveItem), obj_items.end()); + } +// [/RLVa:KB] +// getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false); +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b + // Filter out any new attachments that can't be worn before adding them + if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) + obj_items_new.erase(std::remove_if(obj_items_new.begin(), obj_items_new.end(), RlvPredCanNotWearItem(RLV_WEAR)), obj_items_new.end()); + obj_items.insert(obj_items.end(), obj_items_new.begin(), obj_items_new.end()); +// [/RLVa:KB] + removeDuplicateItems(obj_items); + + // + // - Gestures: include COF contents only if appending. + // + LLInventoryModel::item_array_t gest_items; + if (append) + getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); +// getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false); +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Added: RLVa-1.2.0b + gest_items.insert(gest_items.end(), gest_items_new.begin(), gest_items_new.end()); +// [/RLVa:KB] + removeDuplicateItems(gest_items); + + // Create links to new COF contents. + llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl; + LLPointer link_waiter = new LLUpdateAppearanceOnDestroy(!append); + +// [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f + //if (!append) + { +// [/SL:KB] + // Remove current COF contents. + bool keep_outfit_links = append; + purgeCategory(cof, keep_outfit_links); + gInventory.notifyObservers(); +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking body items" << llendl; +#endif + linkAll(cof, body_items, link_waiter); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking wear items" << llendl; +#endif + linkAll(cof, wear_items, link_waiter); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking obj items" << llendl; +#endif + linkAll(cof, obj_items, link_waiter); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking gesture items" << llendl; +#endif + linkAll(cof, gest_items, link_waiter); +// [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f + } + /*else + { + // Synchronize COF + // -> it's possible that we don't link to any new items in which case 'link_waiter' fires when it goes out of scope below + syncCOF(body_items, LLAssetType::AT_BODYPART, link_waiter); + syncCOF(wear_items, LLAssetType::AT_CLOTHING, link_waiter); + syncCOF(obj_items, LLAssetType::AT_OBJECT, link_waiter); + syncCOF(gest_items, LLAssetType::AT_GESTURE, link_waiter); + gInventory.notifyObservers(); + }*/ +// [/SL:KB] + + // Add link to outfit if category is an outfit. +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Added: RLVa-1.2.0b + /*if ( (!append) && (idOutfit.notNull()) ) + { + createBaseOutfitLink(idOutfit, link_waiter); + }*/ +// [/RLVa:KB] + if (!append) + { + createBaseOutfitLink(idOutfit, link_waiter); + } + llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl; +} + +void LLAppearanceMgr::updatePanelOutfitName(const std::string& name) +{ + // MULTI-WEARABLE TODO + /*LLSidepanelAppearance* panel_appearance = + dynamic_cast(LLFloaterSidePanelContainer::getPanel("appearance")); + if (panel_appearance) + { + panel_appearance->refreshCurrentOutfitName(name); + }*/ +} + +void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer link_waiter) +{ + const LLUUID cof = getCOF(); + LLViewerInventoryCategory* catp = gInventory.getCategory(category); + std::string new_outfit_name = ""; + + purgeBaseOutfitLink(cof); + + if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "", + LLAssetType::AT_LINK_FOLDER, link_waiter); + new_outfit_name = catp->getName(); + } + + updatePanelOutfitName(new_outfit_name); +} + +void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append) +{ + lldebugs << "updateAgentWearables()" << llendl; + LLInventoryItem::item_array_t items; + LLDynamicArray< LLWearable* > wearables; + + // For each wearable type, find the wearables of that type. + for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) + { + for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->getFoundList().begin(); + iter != holder->getFoundList().end(); ++iter) + { + LLFoundData& data = *iter; + LLWearable* wearable = data.mWearable; + if( wearable && ((S32)wearable->getType() == i) ) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID); + if( item && (item->getAssetUUID() == wearable->getAssetID()) ) + { + items.put(item); + wearables.put(wearable); + } + } + } + } + + if(wearables.count() > 0) + { + gAgentWearables.setWearableOutfit(items, wearables, !append); + } + +// dec_busy_count(); +} + +static void remove_non_link_items(LLInventoryModel::item_array_t &items) +{ + LLInventoryModel::item_array_t pruned_items; + for (LLInventoryModel::item_array_t::const_iterator iter = items.begin(); + iter != items.end(); + ++iter) + { + const LLViewerInventoryItem *item = (*iter); + if (item && item->getIsLinkType()) + { + pruned_items.push_back((*iter)); + } + } + items = pruned_items; +} + +//a predicate for sorting inventory items by actual descriptions +bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2) +{ + if (!item1 || !item2) + { + llwarning("either item1 or item2 is NULL", 0); + return true; + } + + return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription(); +} + +void item_array_diff(LLInventoryModel::item_array_t& full_list, + LLInventoryModel::item_array_t& keep_list, + LLInventoryModel::item_array_t& kill_list) + +{ + for (LLInventoryModel::item_array_t::iterator it = full_list.begin(); + it != full_list.end(); + ++it) + { + LLViewerInventoryItem *item = *it; + if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()? + { + kill_list.push_back(item); + } + } +} + +S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id, + LLAssetType::EType type, + S32 max_items, + LLInventoryModel::item_array_t& items_to_kill) +{ + S32 to_kill_count = 0; + + LLInventoryModel::item_array_t items; + getDescendentsOfAssetType(cat_id, items, type, false); + LLInventoryModel::item_array_t curr_items = items; + removeDuplicateItems(items); + if (max_items > 0) + { + filterWearableItems(items, max_items); + } + LLInventoryModel::item_array_t kill_items; + item_array_diff(curr_items,items,kill_items); + for (LLInventoryModel::item_array_t::iterator it = kill_items.begin(); + it != kill_items.end(); + ++it) + { + items_to_kill.push_back(*it); + to_kill_count++; + } + return to_kill_count; +} + + +void LLAppearanceMgr::enforceItemRestrictions() +{ + S32 purge_count = 0; + LLInventoryModel::item_array_t items_to_kill; + + purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART, + 1, items_to_kill); + purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING, + LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill); + purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT, + -1, items_to_kill); + + if (items_to_kill.size()>0) + { + for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin(); + it != items_to_kill.end(); + ++it) + { + LLViewerInventoryItem *item = *it; + llinfos << "purging duplicate or excess item " << item->getName() << llendl; + gInventory.purgeObject(item->getUUID()); + } + gInventory.notifyObservers(); + } +} + +void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) +{ + if (mIsInUpdateAppearanceFromCOF) + { + llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl; + return; + } + + BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF); + + llinfos << "starting" << llendl; + + //checking integrity of the COF in terms of ordering of wearables, + //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state) + updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering); + + // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but + // this should catch anything that gets through. + enforceItemRestrictions(); + + // update dirty flag to see if the state of the COF matches + // the saved outfit stored as a folder link + updateIsDirty(); + + //dumpCat(getCOF(),"COF, start"); + + bool follow_folder_links = true; + LLUUID current_outfit_id = getCOF(); + + // Find all the wearables that are in the COF's subtree. + lldebugs << "LLAppearanceMgr::updateFromCOF()" << llendl; + LLInventoryModel::item_array_t wear_items; + LLInventoryModel::item_array_t obj_items; + LLInventoryModel::item_array_t gest_items; + getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links); + // Get rid of non-links in case somehow the COF was corrupted. + remove_non_link_items(wear_items); + remove_non_link_items(obj_items); + remove_non_link_items(gest_items); + + dumpItemArray(wear_items,"asset_dump: wear_item"); + dumpItemArray(obj_items,"asset_dump: obj_item"); + + if(!wear_items.count()) + { + LLNotificationsUtil::add("CouldNotPutOnOutfit"); + return; + } + + //preparing the list of wearables in the correct order for LLAgentWearables + sortItemsByActualDescription(wear_items); + + + LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; + + holder->setObjItems(obj_items); + holder->setGestItems(gest_items); + + // Note: can't do normal iteration, because if all the + // wearables can be resolved immediately, then the + // callback will be called (and this object deleted) + // before the final getNextData(). + + for(S32 i = 0; i < wear_items.count(); ++i) + { + LLViewerInventoryItem *item = wear_items.get(i); + LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; + + // Fault injection: use debug setting to test asset + // fetch failures (should be replaced by new defaults in + // lost&found). + U32 skip_type = gSavedSettings.getU32("ForceAssetFail"); + + if (item && item->getIsLinkType() && linked_item) + { + LLFoundData found(linked_item->getUUID(), + linked_item->getAssetUUID(), + linked_item->getName(), + linked_item->getType(), + linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID + ); + + if (skip_type != LLWearableType::WT_INVALID && skip_type == found.mWearableType) + { + found.mAssetID.generate(); // Replace with new UUID, guaranteed not to exist in DB + } + //pushing back, not front, to preserve order of wearables for LLAgentWearables + holder->getFoundList().push_back(found); + } + else + { + if (!item) + { + llwarns << "Attempt to wear a null item " << llendl; + } + else if (!linked_item) + { + llwarns << "Attempt to wear a broken link [ name:" << item->getName() << " ] " << llendl; + } + } + } + + for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin(); + it != holder->getFoundList().end(); ++it) + { + LLFoundData& found = *it; + + lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl; + + // Fetch the wearables about to be worn. + LLWearableList::instance().getAsset(found.mAssetID, + found.mName, + found.mAssetType, + onWearableAssetFetch, + (void*)holder); + + } + + holder->resetTime(gSavedSettings.getF32("MaxWearableWaitTime")); + if (!holder->pollFetchCompletion()) + { + doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder)); + } +} + +void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category, + LLInventoryModel::item_array_t& items, + LLAssetType::EType type, + bool follow_folder_links) +{ + LLInventoryModel::cat_array_t cats; + LLIsType is_of_type(type); + gInventory.collectDescendentsIf(category, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + is_of_type, + follow_folder_links); +} + +void LLAppearanceMgr::getUserDescendents(const LLUUID& category, + LLInventoryModel::item_array_t& wear_items, + LLInventoryModel::item_array_t& obj_items, + LLInventoryModel::item_array_t& gest_items, + bool follow_folder_links) +{ + LLInventoryModel::cat_array_t wear_cats; + LLFindWearables is_wearable; + gInventory.collectDescendentsIf(category, + wear_cats, + wear_items, + LLInventoryModel::EXCLUDE_TRASH, + is_wearable, + follow_folder_links); + + LLInventoryModel::cat_array_t obj_cats; + LLIsType is_object( LLAssetType::AT_OBJECT ); + gInventory.collectDescendentsIf(category, + obj_cats, + obj_items, + LLInventoryModel::EXCLUDE_TRASH, + is_object, + follow_folder_links); + + // Find all gestures in this folder + LLInventoryModel::cat_array_t gest_cats; + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + gInventory.collectDescendentsIf(category, + gest_cats, + gest_items, + LLInventoryModel::EXCLUDE_TRASH, + is_gesture, + follow_folder_links); +} + +void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append) +{ + if(!category) return; + + gAgentWearables.notifyLoadingStarted(); + + llinfos << "wearInventoryCategory( " << category->getName() + << " )" << llendl; + + callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, + &LLAppearanceMgr::instance(), + category->getUUID(), copy, append)); +} + +void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append) +{ + llinfos << "starting" << llendl; + + // We now have an outfit ready to be copied to agent inventory. Do + // it, and wear that outfit normally. + LLInventoryCategory* cat = gInventory.getCategory(cat_id); + if(copy_items) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_id, cats, items); + std::string name; + if(!cat) + { + // should never happen. + name = "New Outfit"; + } + else + { + name = cat->getName(); + } + LLViewerInventoryItem* item = NULL; + LLInventoryModel::item_array_t::const_iterator it = items->begin(); + LLInventoryModel::item_array_t::const_iterator end = items->end(); + LLUUID pid; + for(; it < end; ++it) + { + item = *it; + if(item) + { + if(LLInventoryType::IT_GESTURE == item->getInventoryType()) + { + pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); + } + else + { + pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); + } + break; + } + } + if(pid.isNull()) + { + pid = gInventory.getRootFolderID(); + } + + LLUUID new_cat_id = gInventory.createNewCategory( + pid, + LLFolderType::FT_NONE, + name); + LLPointer cb = new LLWearInventoryCategoryCallback(new_cat_id, append); + it = items->begin(); + for(; it < end; ++it) + { + item = *it; + if(item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_id, + std::string(), + cb); + } + } + // BAP fixes a lag in display of created dir. + gInventory.notifyObservers(); + } + else + { + // Wear the inventory category. + LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, append); + } +} + +// *NOTE: hack to get from avatar inventory to avatar +void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* category, bool append ) +{ + // Avoid unintentionally overwriting old wearables. We have to do + // this up front to avoid having to deal with the case of multiple + // wearables being dirty. + if(!category) return; + + llinfos << "wearInventoryCategoryOnAvatar( " << category->getName() + << " )" << llendl; + + if (gAgentCamera.cameraCustomizeAvatar()) + { + // switching to outfit editor should automagically save any currently edited wearable + //LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); // MULTI-WEARABLES TODO + } + + LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append); +} + +void LLAppearanceMgr::wearOutfitByName(const std::string& name) +{ + llinfos << "Wearing category " << name << llendl; + //inc_busy_count(); + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLNameCategoryCollector has_name(name); + gInventory.collectDescendentsIf(gInventory.getRootFolderID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + has_name); + bool copy_items = false; + LLInventoryCategory* cat = NULL; + if (cat_array.count() > 0) + { + // Just wear the first one that matches + cat = cat_array.get(0); + } + else + { + gInventory.collectDescendentsIf(LLUUID::null, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + has_name); + if(cat_array.count() > 0) + { + cat = cat_array.get(0); + copy_items = true; + } + } + + if(cat) + { + LLAppearanceMgr::wearInventoryCategory(cat, copy_items, false); + } + else + { + llwarns << "Couldn't find outfit " <isWearableType() && b->isWearableType() && + (a->getWearableType() == b->getWearableType())); +} + +class LLDeferredCOFLinkObserver: public LLInventoryObserver +{ +public: + LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer cb = NULL): + mItemID(item_id), + mDoUpdate(do_update), + mCallback(cb) + { + } + + ~LLDeferredCOFLinkObserver() + { + } + + /* virtual */ void changed(U32 mask) + { + const LLInventoryItem *item = gInventory.getItem(mItemID); + if (item) + { + gInventory.removeObserver(this); + LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback); + delete this; + } + } + +private: + const LLUUID mItemID; + bool mDoUpdate; + LLPointer mCallback; +}; + + +// BAP - note that this runs asynchronously if the item is not already loaded from inventory. +// Dangerous if caller assumes link will exist after calling the function. +void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer cb) +{ + const LLInventoryItem *item = gInventory.getItem(item_id); + if (!item) + { + LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb); + gInventory.addObserver(observer); + } + else + { + addCOFItemLink(item, do_update, cb); + } +} + +void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer cb) +{ + const LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem) + { + llwarns << "not an llviewerinventoryitem, failed" << llendl; + return; + } + + gInventory.addChangedMask(LLInventoryObserver::LABEL, vitem->getLinkedUUID()); + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(LLAppearanceMgr::getCOF(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + bool linked_already = false; + U32 count = 0; + for (S32 i=0; igetWearableType(); + + const bool is_body_part = (wearable_type == LLWearableType::WT_SHAPE) + || (wearable_type == LLWearableType::WT_HAIR) + || (wearable_type == LLWearableType::WT_EYES) + || (wearable_type == LLWearableType::WT_SKIN); + + if (inv_item->getLinkedUUID() == vitem->getLinkedUUID()) + { + linked_already = true; + } + // Are these links to different items of the same body part + // type? If so, new item will replace old. + else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) + { + ++count; + if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) + { + gInventory.purgeObject(inv_item->getUUID()); + } + else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) + { + // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE + gInventory.purgeObject(inv_item->getUUID()); + } +// [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e + else if ( (vitem->getWearableType() == wearable_type) && (vitem->getAssetUUID() == inv_item->getAssetUUID()) ) + { + // Only allow one wearable per unique asset + linked_already = true; + } +// [/SL:KB] + } + } + + if (linked_already) + { + if (do_update) + { + LLAppearanceMgr::updateAppearanceFromCOF(); + } + return; + } + else + { + if(do_update && cb.isNull()) + { + cb = new ModifiedCOFCallback; + } + const std::string description = vitem->getIsLinkType() ? vitem->getDescription() : ""; + link_inventory_item( gAgent.getID(), + vitem->getLinkedUUID(), + getCOF(), + vitem->getName(), + description, + LLAssetType::AT_LINK, + cb); + } + return; +} + +// BAP remove ensemble code for 2.1? +void LLAppearanceMgr::addEnsembleLink( LLInventoryCategory* cat, bool do_update ) +{ +#if SUPPORT_ENSEMBLES + // BAP add check for already in COF. + LLPointer cb = do_update ? new ModifiedCOFCallback : 0; + link_inventory_item( gAgent.getID(), + cat->getLinkedUUID(), + getCOF(), + cat->getName(), + cat->getDescription(), + LLAssetType::AT_LINK_FOLDER, + cb); +#endif +} + +void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update) +{ + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(LLAppearanceMgr::getCOF(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + for (S32 i=0; igetIsLinkType() && item->getLinkedUUID() == item_id) + { + gInventory.purgeObject(item->getUUID()); + } + } + if (do_update) + { + LLAppearanceMgr::updateAppearanceFromCOF(); + } +} + +void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update) +{ + LLFindWearablesOfType filter_wearables_of_type(type); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryModel::item_array_t::const_iterator it; + + gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); + for (it = items.begin(); it != items.end(); ++it) + { + const LLViewerInventoryItem* item = *it; + if (item->getIsLinkType()) // we must operate on links only + { + gInventory.purgeObject(item->getUUID()); + } + } + + if (do_update) + { + updateAppearanceFromCOF(); + } +} + +bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2) +{ + if (!item1 || !item2) + { + llwarning("item1, item2 cannot be null, something is very wrong", 0); + return true; + } + + return item1->getLinkedUUID() < item2->getLinkedUUID(); +} + +void LLAppearanceMgr::updateIsDirty() +{ + LLUUID cof = getCOF(); + LLUUID base_outfit; + + // find base outfit link + const LLViewerInventoryItem* base_outfit_item = getBaseOutfitLink(); + LLViewerInventoryCategory* catp = NULL; + if (base_outfit_item && base_outfit_item->getIsLinkType()) + { + catp = base_outfit_item->getLinkedCategory(); + } + if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + base_outfit = catp->getUUID(); + } + + // Set dirty to "false" if no base outfit found to disable "Save" + // and leave only "Save As" enabled in My Outfits. + mOutfitIsDirty = false; + + if (base_outfit.notNull()) + { + LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK); + + LLInventoryModel::cat_array_t cof_cats; + LLInventoryModel::item_array_t cof_items; + gInventory.collectDescendentsIf(cof, cof_cats, cof_items, + LLInventoryModel::EXCLUDE_TRASH, collector); + + LLInventoryModel::cat_array_t outfit_cats; + LLInventoryModel::item_array_t outfit_items; + gInventory.collectDescendentsIf(base_outfit, outfit_cats, outfit_items, + LLInventoryModel::EXCLUDE_TRASH, collector); + + if(outfit_items.count() != cof_items.count()) + { + // Current outfit folder should have one more item than the outfit folder. + // this one item is the link back to the outfit folder itself. + mOutfitIsDirty = true; + return; + } + + //"dirty" - also means a difference in linked UUIDs and/or a difference in wearables order (links' descriptions) + std::sort(cof_items.begin(), cof_items.end(), sort_by_linked_uuid); + std::sort(outfit_items.begin(), outfit_items.end(), sort_by_linked_uuid); + + for (U32 i = 0; i < cof_items.size(); ++i) + { + LLViewerInventoryItem *item1 = cof_items.get(i); + LLViewerInventoryItem *item2 = outfit_items.get(i); + + if (item1->getLinkedUUID() != item2->getLinkedUUID() || + item1->getName() != item2->getName() || + item1->LLInventoryItem::getDescription() != item2->LLInventoryItem::getDescription()) + { + mOutfitIsDirty = true; + return; + } + } + } +} + +// *HACK: Must match name in Library or agent inventory +const std::string ROOT_GESTURES_FOLDER = "Gestures"; +const std::string COMMON_GESTURES_FOLDER = "Common Gestures"; +const std::string MALE_GESTURES_FOLDER = "Male Gestures"; +const std::string FEMALE_GESTURES_FOLDER = "Female Gestures"; +const std::string SPEECH_GESTURES_FOLDER = "Speech Gestures"; +const std::string OTHER_GESTURES_FOLDER = "Other Gestures"; + +void LLAppearanceMgr::copyLibraryGestures() +{ + llinfos << "Copying library gestures" << llendl; + + // Copy gestures + LLUUID lib_gesture_cat_id = + gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE,false,true); + if (lib_gesture_cat_id.isNull()) + { + llwarns << "Unable to copy gestures, source category not found" << llendl; + } + LLUUID dst_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); + + std::vector gesture_folders_to_copy; + gesture_folders_to_copy.push_back(MALE_GESTURES_FOLDER); + gesture_folders_to_copy.push_back(FEMALE_GESTURES_FOLDER); + gesture_folders_to_copy.push_back(COMMON_GESTURES_FOLDER); + gesture_folders_to_copy.push_back(SPEECH_GESTURES_FOLDER); + gesture_folders_to_copy.push_back(OTHER_GESTURES_FOLDER); + + for(std::vector::iterator it = gesture_folders_to_copy.begin(); + it != gesture_folders_to_copy.end(); + ++it) + { + std::string& folder_name = *it; + + LLPointer cb(NULL); + + // After copying gestures, activate Common, Other, plus + // Male and/or Female, depending upon the initial outfit gender. + ESex gender = gAgentAvatarp->getSex(); + + std::string activate_male_gestures; + std::string activate_female_gestures; + switch (gender) { + case SEX_MALE: + activate_male_gestures = MALE_GESTURES_FOLDER; + break; + case SEX_FEMALE: + activate_female_gestures = FEMALE_GESTURES_FOLDER; + break; + case SEX_BOTH: + activate_male_gestures = MALE_GESTURES_FOLDER; + activate_female_gestures = FEMALE_GESTURES_FOLDER; + break; + } + + if (folder_name == activate_male_gestures || + folder_name == activate_female_gestures || + folder_name == COMMON_GESTURES_FOLDER || + folder_name == OTHER_GESTURES_FOLDER) + { + cb = new ActivateGestureCallback; + } + + LLUUID cat_id = findDescendentCategoryIDByName(lib_gesture_cat_id,folder_name); + if (cat_id.isNull()) + { + llwarns << "failed to find gesture folder for " << folder_name << llendl; + } + else + { + llinfos << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << llendl; + callAfterCategoryFetch(cat_id, + boost::bind(&LLAppearanceMgr::shallowCopyCategory, + &LLAppearanceMgr::instance(), + cat_id, dst_id, cb)); + } + } +} + +void LLAppearanceMgr::autopopulateOutfits() +{ + // If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account) + // then auto-populate outfits from the library into the My Outfits folder. + + llinfos << "avatar fully visible" << llendl; + + static bool check_populate_my_outfits = true; + if (check_populate_my_outfits && + (LLInventoryModel::getIsFirstTimeInViewer2() + || gSavedSettings.getBOOL("MyOutfitsAutofill"))) + { + gAgentWearables.populateMyOutfitsFolder(); + } + check_populate_my_outfits = false; +} + +// Handler for anything that's deferred until avatar de-clouds. +void LLAppearanceMgr::onFirstFullyVisible() +{ + gAgentAvatarp->outputRezTiming("Avatar fully loaded"); + gAgentAvatarp->reportAvatarRezTime(); + gAgentAvatarp->debugAvatarVisible(); + + // The auto-populate is failing at the point of generating outfits + // folders, so don't do the library copy until that is resolved. + // autopopulateOutfits(); + + // If this is the first time we've ever logged in, + // then copy default gestures from the library. + if (gAgent.isFirstLogin()) { + copyLibraryGestures(); + } +} + +bool LLAppearanceMgr::updateBaseOutfit() +{ + if (isOutfitLocked()) + { + // don't allow modify locked outfit + llassert(!isOutfitLocked()); + return false; + } + setOutfitLocked(true); + + gAgentWearables.notifyLoadingStarted(); + + const LLUUID base_outfit_id = getBaseOutfitUUID(); + if (base_outfit_id.isNull()) return false; + + updateClothingOrderingInfo(); + + // in a Base Outfit we do not remove items, only links + purgeCategory(base_outfit_id, false); + + + LLPointer dirty_state_updater = new LLUpdateDirtyState(); + + //COF contains only links so we copy to the Base Outfit only links + shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater); + + return true; +} + +void LLAppearanceMgr::divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type) +{ + items_by_type.resize(LLWearableType::WT_COUNT); + if (items.empty()) return; + + for (S32 i=0; iisWearableType()) + continue; + LLWearableType::EType type = item->getWearableType(); + if(type < 0 || type >= LLWearableType::WT_COUNT) + { + LL_WARNS("Appearance") << "Invalid wearable type. Inventory type does not match wearable flag bitfield." << LL_ENDL; + continue; + } + items_by_type[type].push_back(item); + } +} + +std::string build_order_string(LLWearableType::EType type, U32 i) +{ + std::ostringstream order_num; + order_num << ORDER_NUMBER_SEPARATOR << type * 100 + i; + return order_num.str(); +} + +struct WearablesOrderComparator +{ + LOG_CLASS(WearablesOrderComparator); + WearablesOrderComparator(const LLWearableType::EType type) + { + mControlSize = build_order_string(type, 0).size(); + }; + + bool operator()(const LLInventoryItem* item1, const LLInventoryItem* item2) + { + if (!item1 || !item2) + { + llwarning("either item1 or item2 is NULL", 0); + return true; + } + + const std::string& desc1 = item1->LLInventoryItem::getDescription(); + const std::string& desc2 = item2->LLInventoryItem::getDescription(); + + bool item1_valid = (desc1.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc1[0]); + bool item2_valid = (desc2.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc2[0]); + + if (item1_valid && item2_valid) + return desc1 < desc2; + + //we need to sink down invalid items: items with empty descriptions, items with "Broken link" descriptions, + //items with ordering information but not for the associated wearables type + if (!item1_valid && item2_valid) + return false; + + return true; + } + + U32 mControlSize; +}; + +void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base_outfit_ordering) +{ + if (cat_id.isNull()) + { + cat_id = getCOF(); + if (update_base_outfit_ordering) + { + const LLUUID base_outfit_id = getBaseOutfitUUID(); + if (base_outfit_id.notNull()) + { + updateClothingOrderingInfo(base_outfit_id,false); + } + } + } + + // COF is processed if cat_id is not specified + LLInventoryModel::item_array_t wear_items; + getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING, false); + + wearables_by_type_t items_by_type(LLWearableType::WT_COUNT); + divvyWearablesByType(wear_items, items_by_type); + + bool inventory_changed = false; + for (U32 type = LLWearableType::WT_SHIRT; type < LLWearableType::WT_COUNT; type++) + { + + U32 size = items_by_type[type].size(); + if (!size) continue; + + //sinking down invalid items which need reordering + std::sort(items_by_type[type].begin(), items_by_type[type].end(), WearablesOrderComparator((LLWearableType::EType) type)); + + //requesting updates only for those links which don't have "valid" descriptions + for (U32 i = 0; i < size; i++) + { + LLViewerInventoryItem* item = items_by_type[type][i]; + if (!item) continue; + + std::string new_order_str = build_order_string((LLWearableType::EType)type, i); + if (new_order_str == item->LLInventoryItem::getDescription()) continue; + + item->setDescription(new_order_str); + item->setComplete(TRUE); + item->updateServer(FALSE); + gInventory.updateItem(item); + + inventory_changed = true; + } + } + + //*TODO do we really need to notify observers? + if (inventory_changed) gInventory.notifyObservers(); +} + + + + +class LLShowCreatedOutfit: public LLInventoryCallback +{ +public: + LLShowCreatedOutfit(LLUUID& folder_id, bool show_panel = true): mFolderID(folder_id), mShowPanel(show_panel) + {} + + virtual ~LLShowCreatedOutfit() + { + if (!LLApp::isRunning()) + { + llwarns << "called during shutdown, skipping" << llendl; + return; + } + + LLSD key; + + //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process + // add may be processed after login process is finished + // MULTI-WEARABLES TODO + /*if (mShowPanel) + { + LLFloaterSidePanelContainer::showPanel("appearance", "panel_outfits_inventory", key); + + } + LLOutfitsList *outfits_list = + dynamic_cast(LLFloaterSidePanelContainer::getPanel("appearance", "outfitslist_tab")); + if (outfits_list) + { + outfits_list->setSelectedOutfitByUUID(mFolderID); + }*/ + + LLAppearanceMgr::getInstance()->updateIsDirty(); + gAgentWearables.notifyLoadingFinished(); // New outfit is saved. + LLAppearanceMgr::getInstance()->updatePanelOutfitName(""); + } + + virtual void fire(const LLUUID&) + {} + +private: + LLUUID mFolderID; + bool mShowPanel; +}; + +LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel) +{ + if (!isAgentAvatarValid()) return LLUUID::null; + + gAgentWearables.notifyLoadingStarted(); + + // First, make a folder in the My Outfits directory. + const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + LLUUID folder_id = gInventory.createNewCategory( + parent_id, + LLFolderType::FT_OUTFIT, + new_folder_name); + + updateClothingOrderingInfo(); + + LLPointer cb = new LLShowCreatedOutfit(folder_id,show_panel); + shallowCopyCategoryContents(getCOF(),folder_id, cb); + createBaseOutfitLink(folder_id, cb); + + dumpCat(folder_id,"COF, new outfit"); + + return folder_id; +} + +void LLAppearanceMgr::wearBaseOutfit() +{ + const LLUUID& base_outfit_id = getBaseOutfitUUID(); + if (base_outfit_id.isNull()) return; + + updateCOF(base_outfit_id); +} + +void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) +{ + LLViewerInventoryItem * item_to_remove = gInventory.getItem(id_to_remove); + if (!item_to_remove) return; + + switch (item_to_remove->getType()) + { + case LLAssetType::AT_CLOTHING: +// if (get_is_item_worn(id_to_remove)) +// { +// //*TODO move here the exact removing code from LLWearableBridge::removeItemFromAvatar in the future +// LLWearableBridge::removeItemFromAvatar(item_to_remove); +// } +// [SL:KB] - Patch: Appearance-RemoveWearableFromAvatar | Checked: 2010-08-13 (Catznip-3.0.0a) | Added: Catznip-2.1.1d +// [RLVa:KB] - Checked: 2010-09-04 (RLVa-1.2.1c) | Added: RLVa-1.2.1c + if ( (!rlv_handler_t::isEnabled()) || (gRlvWearableLocks.canRemove(item_to_remove)) ) +// [/RLVa:KB] + { + const LLWearable* pWearable = gAgentWearables.getWearableFromItemID(item_to_remove->getLinkedUUID()); + if ( (pWearable) && (LLAssetType::AT_BODYPART != pWearable->getAssetType()) ) + { + U32 idxWearable = gAgentWearables.getWearableIndex(pWearable); + if (idxWearable < LLAgentWearables::MAX_CLOTHING_PER_TYPE) + { + gAgentWearables.removeWearable(pWearable->getType(), false, idxWearable); + + LLAppearanceMgr::instance().removeCOFItemLinks(item_to_remove->getLinkedUUID(), false); + gInventory.notifyObservers(); + +// [RLVa:KB] - Checked: 2011-06-07 (RLVa-1.3.1b) | Added: RLVa-1.3.1b + //RlvBehaviourNotifyHandler::onTakeOff(pWearable->getType(), true); +// [/RLVa:KB] + } + } + } +// [/SL:KB] + break; + case LLAssetType::AT_OBJECT: + LLVOAvatarSelf::detachAttachmentIntoInventory(item_to_remove->getLinkedUUID()); + default: + break; + } + + // *HACK: Force to remove garbage from COF. + // Unworn links or objects can't be processed by existed removing functionality + // since it is not designed for such cases. As example attachment object can't be removed + // since sever don't sends message _PREHASH_KillObject in that case. + // Also we can't check is link was successfully removed from COF since in case + // deleting attachment link removing performs asynchronously in process_kill_object callback. + removeCOFItemLinks(id_to_remove,false); +} + +bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) +{ + if (!item || !item->isWearableType()) return false; + if (item->getType() != LLAssetType::AT_CLOTHING) return false; + if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false; + + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesOfType filter_wearables_of_type(item->getWearableType()); + gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); + if (items.empty()) return false; + + // We assume that the items have valid descriptions. + std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); + + if (closer_to_body && items.front() == item) return false; + if (!closer_to_body && items.back() == item) return false; + + LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item); + if (items.end() == it) return false; + + + //swapping descriptions + closer_to_body ? --it : ++it; + LLViewerInventoryItem* swap_item = *it; + if (!swap_item) return false; + std::string tmp = swap_item->LLInventoryItem::getDescription(); + swap_item->setDescription(item->LLInventoryItem::getDescription()); + item->setDescription(tmp); + + + //items need to be updated on a dataserver + item->setComplete(TRUE); + item->updateServer(FALSE); + gInventory.updateItem(item); + + swap_item->setComplete(TRUE); + swap_item->updateServer(FALSE); + gInventory.updateItem(swap_item); + + //to cause appearance of the agent to be updated + bool result = false; + if (result = gAgentWearables.moveWearable(item, closer_to_body)) + { + gAgentAvatarp->wearableUpdated(item->getWearableType(), FALSE); + } + + setOutfitDirty(true); + + //*TODO do we need to notify observers here in such a way? + gInventory.notifyObservers(); + + return result; +} + +//static +void LLAppearanceMgr::sortItemsByActualDescription(LLInventoryModel::item_array_t& items) +{ + if (items.size() < 2) return; + + std::sort(items.begin(), items.end(), sort_by_description); +} + +//#define DUMP_CAT_VERBOSE + +void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + +#ifdef DUMP_CAT_VERBOSE + llinfos << llendl; + llinfos << str << llendl; + S32 hitcount = 0; + for(S32 i=0; igetName() <getLinkedItem() : NULL; + LLUUID asset_id; + if (linked_item) + { + asset_id = linked_item->getAssetUUID(); + } + llinfos << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << llendl; + } + llinfos << llendl; +} + +LLAppearanceMgr::LLAppearanceMgr(): + mAttachmentInvLinkEnabled(false), + mOutfitIsDirty(false), + mOutfitLocked(false), + mIsInUpdateAppearanceFromCOF(false) +{ + LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); + + // unlock outfit on save operation completed + outfit_observer.addCOFSavedCallback(boost::bind( + &LLAppearanceMgr::setOutfitLocked, this, false)); + + mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32( + "OutfitOperationsTimeout"))); + + gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle,NULL); +} + +LLAppearanceMgr::~LLAppearanceMgr() +{ +} + +void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val) +{ + llinfos << "setAttachmentInvLinkEnable => " << (int) val << llendl; + mAttachmentInvLinkEnabled = val; +} + +void dumpAttachmentSet(const std::set& atts, const std::string& msg) +{ + llinfos << msg << llendl; + for (std::set::const_iterator it = atts.begin(); + it != atts.end(); + ++it) + { + LLUUID item_id = *it; + LLViewerInventoryItem *item = gInventory.getItem(item_id); + if (item) + llinfos << "atts " << item->getName() << llendl; + else + llinfos << "atts " << "UNKNOWN[" << item_id.asString() << "]" << llendl; + } + llinfos << llendl; +} + +void LLAppearanceMgr::registerAttachment(const LLUUID& item_id) +{ + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + + if (mAttachmentInvLinkEnabled) + { + // we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF. + // it will trigger gAgentWariables.notifyLoadingFinished() + // But it is not acceptable solution. See EXT-7777 + LLAppearanceMgr::addCOFItemLink(item_id, false); // Add COF link for item. + } + else + { + //llinfos << "no link changes, inv link not enabled" << llendl; + } +} + +void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id) +{ + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + + if (mAttachmentInvLinkEnabled) + { + LLAppearanceMgr::removeCOFItemLinks(item_id, false); + } + else + { + //llinfos << "no link changes, inv link not enabled" << llendl; + } +} + +BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const +{ + return gInventory.isObjectDescendentOf(obj_id, getCOF()); +} + +// static +bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLLinkedItemIDMatches find_links(gInventory.getLinkedItemID(obj_id)); + gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + find_links); + + return !items.empty(); +} + +BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const +{ + if (!getIsInCOF(obj_id)) return FALSE; + + // If a non-link somehow ended up in COF, allow deletion. + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (obj && !obj->getIsLinkType()) + { + return FALSE; + } + + // For now, don't allow direct deletion from the COF. Instead, force users + // to choose "Detach" or "Take Off". + return TRUE; + /* + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (!obj) return FALSE; + + // Can't delete bodyparts, since this would be equivalent to removing the item. + if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE; + + // Can't delete the folder link, since this is saved for bookkeeping. + if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE; + + return FALSE; + */ +} + +class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver +{ +public: + CallAfterCategoryFetchStage2(const uuid_vec_t& ids, + nullary_func_t callable) : + LLInventoryFetchItemsObserver(ids), + mCallable(callable) + { + } + ~CallAfterCategoryFetchStage2() + { + } + virtual void done() + { + llinfos << this << " done with incomplete " << mIncomplete.size() + << " complete " << mComplete.size() << " calling callable" << llendl; + + gInventory.removeObserver(this); + doOnIdleOneTime(mCallable); + delete this; + } +protected: + nullary_func_t mCallable; +}; + +class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver +{ +public: + CallAfterCategoryFetchStage1(const LLUUID& cat_id, nullary_func_t callable) : + LLInventoryFetchDescendentsObserver(cat_id), + mCallable(callable) + { + } + ~CallAfterCategoryFetchStage1() + { + } + virtual void done() + { + // What we do here is get the complete information on the items in + // the library, and set up an observer that will wait for that to + // happen. + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(mComplete.front(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + S32 count = item_array.count(); + if(!count) + { + llwarns << "Nothing fetched in category " << mComplete.front() + << llendl; + //dec_busy_count(); + gInventory.removeObserver(this); + + // lets notify observers that loading is finished. + gAgentWearables.notifyLoadingFinished(); + delete this; + return; + } + + llinfos << "stage1 got " << item_array.count() << " items, passing to stage2 " << llendl; + uuid_vec_t ids; + for(S32 i = 0; i < count; ++i) + { + ids.push_back(item_array.get(i)->getUUID()); + } + + gInventory.removeObserver(this); + + // do the fetch + CallAfterCategoryFetchStage2 *stage2 = new CallAfterCategoryFetchStage2(ids, mCallable); + stage2->startFetch(); + if(stage2->isFinished()) + { + // everything is already here - call done. + stage2->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(stage2); + } + delete this; + } +protected: + nullary_func_t mCallable; +}; + +void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb) +{ + CallAfterCategoryFetchStage1 *stage1 = new CallAfterCategoryFetchStage1(cat_id, cb); + stage1->startFetch(); + if (stage1->isFinished()) + { + stage1->done(); + } + else + { + gInventory.addObserver(stage1); + } +} + +void wear_multiple(const uuid_vec_t& ids, bool replace) +{ + LLPointer cb = new LLUpdateAppearanceOnDestroy; + + bool first = true; + uuid_vec_t::const_iterator it; + for (it = ids.begin(); it != ids.end(); ++it) + { + // if replace is requested, the first item worn will replace the current top + // item, and others will be added. + LLAppearanceMgr::instance().wearItemOnAvatar(*it,false,first && replace,cb); + first = false; + } +} + +// SLapp for easy-wearing of a stock (library) avatar +// +/* +class LLWearFolderHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLWearFolderHandler() : LLCommandHandler("wear_folder", UNTRUSTED_BLOCK) { } + + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + LLPointer category = new LLInventoryCategory(query_map["folder_id"], + LLUUID::null, + LLFolderType::FT_CLOTHING, + "Quick Appearance"); + LLSD::UUID folder_uuid = query_map["folder_id"].asUUID(); + if ( gInventory.getCategory( folder_uuid ) != NULL ) + { + LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); + + // *TODOw: This may not be necessary if initial outfit is chosen already -- josh + gAgent.setGenderChosen(TRUE); + } + + // release avatar picker keyboard focus + gFocusMgr.setKeyboardFocus( NULL ); + + return true; + } +}; + +LLWearFolderHandler gWearFolderHandler;*/ diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h new file mode 100644 index 000000000..6b072075e --- /dev/null +++ b/indra/newview/llappearancemgr.h @@ -0,0 +1,265 @@ +/** + * @file llappearancemgr.h + * @brief Manager for initiating appearance changes on the viewer + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLAPPEARANCEMGR_H +#define LL_LLAPPEARANCEMGR_H + +#include "llsingleton.h" + +#include "llagentwearables.h" +#include "llcallbacklist.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llviewerinventory.h" + +class LLWearable; +class LLWearableHoldingPattern; +class LLInventoryCallback; +class LLOutfitUnLockTimer; + +class LLAppearanceMgr: public LLSingleton +{ + LOG_CLASS(LLAppearanceMgr); + + friend class LLSingleton; + friend class LLOutfitUnLockTimer; + +public: + typedef std::vector wearables_by_type_t; + + void updateAppearanceFromCOF(bool update_base_outfit_ordering = false); + bool needToSaveCOF(); + void updateCOF(const LLUUID& category, bool append = false); +// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0a) | Added: RLVa-1.2.0a + void updateCOF(LLInventoryModel::item_array_t& body_items_new, LLInventoryModel::item_array_t& wear_items_new, + LLInventoryModel::item_array_t& obj_items_new, LLInventoryModel::item_array_t& gest_items_new, + bool append = false, const LLUUID& idOutfit = LLUUID::null); +// [/RLVa:KB] + void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append); + void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append); + void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append); + void wearOutfitByName(const std::string& name); + void changeOutfit(bool proceed, const LLUUID& category, bool append); + void replaceCurrentOutfit(const LLUUID& new_outfit); + void renameOutfit(const LLUUID& outfit_id); + void takeOffOutfit(const LLUUID& cat_id); + void addCategoryToCurrentOutfit(const LLUUID& cat_id); + S32 findExcessOrDuplicateItems(const LLUUID& cat_id, + LLAssetType::EType type, + S32 max_items, + LLInventoryModel::item_array_t& items_to_kill); + void enforceItemRestrictions(); + + // Copy all items and the src category itself. + void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer cb); + + // Return whether this folder contains minimal contents suitable for making a full outfit. + BOOL getCanMakeFolderIntoOutfit(const LLUUID& folder_id); + + // Determine whether a given outfit can be removed. + bool getCanRemoveOutfit(const LLUUID& outfit_cat_id); + + // Determine whether we're wearing any of the outfit contents (excluding body parts). + static bool getCanRemoveFromCOF(const LLUUID& outfit_cat_id); + + // Determine whether we can add anything (but body parts) from the outfit contents to COF. + static bool getCanAddToCOF(const LLUUID& outfit_cat_id); + + // Determine whether we can replace current outfit with the given one. + bool getCanReplaceCOF(const LLUUID& outfit_cat_id); + + // Copy all items in a category. + void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer cb); + + // Find the Current Outfit folder. + const LLUUID getCOF() const; + + // Finds the folder link to the currently worn outfit + const LLViewerInventoryItem *getBaseOutfitLink(); + bool getBaseOutfitName(std::string &name); + + // find the UUID of the currently worn outfit (Base Outfit) + const LLUUID getBaseOutfitUUID(); + + // Wear/attach an item (from a user's inventory) on the agent + bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false, LLPointer cb = NULL); + + // Update the displayed outfit name in UI. + void updatePanelOutfitName(const std::string& name); + + void createBaseOutfitLink(const LLUUID& category, LLPointer link_waiter); + + void updateAgentWearables(LLWearableHoldingPattern* holder, bool append); + + // For debugging - could be moved elsewhere. + void dumpCat(const LLUUID& cat_id, const std::string& msg); + void dumpItemArray(const LLInventoryModel::item_array_t& items, const std::string& msg); + + // Attachment link management + void unregisterAttachment(const LLUUID& item_id); + void registerAttachment(const LLUUID& item_id); + void setAttachmentInvLinkEnable(bool val); + + // utility function for bulk linking. + void linkAll(const LLUUID& category, + LLInventoryModel::item_array_t& items, + LLPointer cb); + + // Add COF link to individual item. + void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer cb = NULL); + void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer cb = NULL); + + // Remove COF entries + void removeCOFItemLinks(const LLUUID& item_id, bool do_update = true); + void removeCOFLinksOfType(LLWearableType::EType type, bool do_update = true); + + // Add COF link to ensemble folder. + void addEnsembleLink(LLInventoryCategory* item, bool do_update = true); + + //has the current outfit changed since it was loaded? + bool isOutfitDirty() { return mOutfitIsDirty; } + + // set false if you just loaded the outfit, true otherwise + void setOutfitDirty(bool isDirty) { mOutfitIsDirty = isDirty; } + + // manually compare ouftit folder link to COF to see if outfit has changed. + // should only be necessary to do on initial login. + void updateIsDirty(); + + // Called when self avatar is first fully visible. + void onFirstFullyVisible(); + + // Create initial outfits from library. + void autopopulateOutfits(); + + // Copy initial gestures from library. + void copyLibraryGestures(); + + void wearBaseOutfit(); + + // Overrides the base outfit with the content from COF + // @return false if there is no base outfit + bool updateBaseOutfit(); + + //Remove clothing or detach an object from the agent (a bodypart cannot be removed) + void removeItemFromAvatar(const LLUUID& item_id); + + + LLUUID makeNewOutfitLinks(const std::string& new_folder_name,bool show_panel = true); + + bool moveWearable(LLViewerInventoryItem* item, bool closer_to_body); + + static void sortItemsByActualDescription(LLInventoryModel::item_array_t& items); + + //Divvy items into arrays by wearable type + static void divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type); + + //Check ordering information on wearables stored in links' descriptions and update if it is invalid + // COF is processed if cat_id is not specified + void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null, bool update_base_outfit_ordering = false); + + bool isOutfitLocked() { return mOutfitLocked; } + + bool isInUpdateAppearanceFromCOF() { return mIsInUpdateAppearanceFromCOF; } + +protected: + LLAppearanceMgr(); + ~LLAppearanceMgr(); + +private: + + void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type); + + void getDescendentsOfAssetType(const LLUUID& category, + LLInventoryModel::item_array_t& items, + LLAssetType::EType type, + bool follow_folder_links); + + void getUserDescendents(const LLUUID& category, + LLInventoryModel::item_array_t& wear_items, + LLInventoryModel::item_array_t& obj_items, + LLInventoryModel::item_array_t& gest_items, + bool follow_folder_links); + + void purgeCategory(const LLUUID& category, bool keep_outfit_links); + void purgeBaseOutfitLink(const LLUUID& category); + + static void onOutfitRename(const LLSD& notification, const LLSD& response); + + void setOutfitLocked(bool locked); + + bool mAttachmentInvLinkEnabled; + bool mOutfitIsDirty; + bool mIsInUpdateAppearanceFromCOF; // to detect recursive calls. + + /** + * Lock for blocking operations on outfit until server reply or timeout exceed + * to avoid unsynchronized outfit state or performing duplicate operations. + */ + bool mOutfitLocked; + + std::auto_ptr mUnlockOutfitTimer; + + ////////////////////////////////////////////////////////////////////////////////// + // Item-specific convenience functions +public: + // Is this in the COF? + BOOL getIsInCOF(const LLUUID& obj_id) const; + // Is this in the COF and can the user delete it from the COF? + BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const; + + /** + * Checks if COF contains link to specified object. + */ + static bool isLinkInCOF(const LLUUID& obj_id); +}; + +class LLUpdateAppearanceOnDestroy: public LLInventoryCallback +{ +public: + LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering = false); + virtual ~LLUpdateAppearanceOnDestroy(); + /* virtual */ void fire(const LLUUID& inv_item); + +private: + U32 mFireCount; + bool mUpdateBaseOrder; +}; + + +#define SUPPORT_ENSEMBLES 0 + +LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name); + +// Invoke a given callable after category contents are fully fetched. +void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb); + +// Wear all items in a uuid vector. +void wear_multiple(const uuid_vec_t& ids, bool replace); + +#endif diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9ed1c25f4..a37af3ad3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3583,7 +3583,7 @@ void LLAppViewer::badNetworkHandler() std::string grid_support_msg = ""; if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty()) { - grid_support_msg = "\n\nOr visit the gird support page at: \n " + grid_support_msg = "\n\nOr visit the grid support page at: \n " + gHippoGridManager->getCurrentGrid()->getSupportUrl(); } std::ostringstream message; @@ -3915,7 +3915,7 @@ void LLAppViewer::idle() return; } - gViewerWindow->handlePerFrameHover(); + gViewerWindow->updateUI(); /////////////////////////////////////// // Agent and camera movement @@ -4642,13 +4642,9 @@ void LLAppViewer::handleLoginComplete() { gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); } - writeDebugInfo(); -// [RLVa:KB] - Checked: 2010-09-27 (RLVa-1.1.3b) | Modified: RLVa-1.1.3b - if (rlv_handler_t::isEnabled()) - { - gRlvHandler.onLoginComplete(); - } -// [/RLVa:KB] + mOnLoginCompleted(); + + writeDebugInfo(); } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index f268324d7..1b0e5614f 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -160,6 +160,11 @@ public: void handleLoginComplete(); LLAllocator & getAllocator() { return mAlloc; } + // On LoginCompleted callback + typedef boost::signals2::signal login_completed_signal_t; + login_completed_signal_t mOnLoginCompleted; + boost::signals2::connection setOnLoginCompletedCallback( const login_completed_signal_t::slot_type& cb ) { return mOnLoginCompleted.connect(cb); } + void addOnIdleCallback(const boost::function& cb); // add a callback to fire (once) when idle void purgeCache(); // Clear the local cache. diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index 757252286..a9ff6e3f7 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -34,19 +34,12 @@ #include "llassetuploadresponders.h" -// library includes -#include "lleconomy.h" -#include "llfocusmgr.h" -#include "llnotifications.h" -#include "llscrolllistctrl.h" -#include "llsdserialize.h" - // viewer includes #include "llagent.h" #include "llcompilequeue.h" #include "llfloaterbuycurrency.h" -#include "llnotify.h" -#include "llinventorymodel.h" +#include "llinventorydefines.h" +#include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llpanelmaininventory.h" #include "llpermissionsflags.h" @@ -63,6 +56,18 @@ #include "llviewermenufile.h" #include "llviewerwindow.h" #include "lltexlayer.h" +#include "lltrans.h" + +// library includes +#include "lldir.h" +#include "lleconomy.h" +#include "llfocusmgr.h" +#include "llnotificationsutil.h" +#include "llscrolllistctrl.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llvfs.h" + #include "statemachine/aifilepicker.h" // When uploading multiple files, don't display any of them when uploading more than this number. @@ -70,14 +75,15 @@ static const S32 FILE_COUNT_DISPLAY_THRESHOLD = 5; void dialog_refresh_all(); -void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - const std::string inventory_type_string, - const LLUUID& item_folder_id, - const std::string& item_name, - const std::string& item_description, - const LLSD& server_response, - S32 upload_price) +void on_new_single_inventory_upload_complete( + LLAssetType::EType asset_type, + LLInventoryType::EType inventory_type, + const std::string inventory_type_string, + const LLUUID& item_folder_id, + const std::string& item_name, + const std::string& item_description, + const LLSD& server_response, + S32 upload_price) { if (upload_price > 0) { @@ -87,7 +93,7 @@ void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type, LLSD args; args["AMOUNT"] = llformat("%d", upload_price); - LLNotifications::instance().add("UploadPayment", args); + LLNotificationsUtil::add("UploadPayment", args); } if (item_folder_id.notNull()) @@ -115,9 +121,18 @@ void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type, } LLPermissions new_perms; - new_perms.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); - new_perms.initMasks(PERM_ALL, PERM_ALL, everyone_perms, group_perms, - next_owner_perms); + new_perms.init( + gAgent.getID(), + gAgent.getID(), + LLUUID::null, + LLUUID::null); + + new_perms.initMasks( + PERM_ALL, + PERM_ALL, + everyone_perms, + group_perms, + next_owner_perms); U32 inventory_item_flags = 0; if (server_response.has("inventory_flags")) @@ -147,17 +162,18 @@ void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type, // Show the preview panel for textures and sounds to let // user know that the image (or snapshot) arrived intact. - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if (view) + LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel(); + if ( panel ) { LLFocusableElement* focus = gFocusMgr.getKeyboardFocus(); - view->getPanel()->setSelection(server_response["new_inventory_item"].asUUID(), - TAKE_FOCUS_NO); + panel->setSelection( + server_response["new_inventory_item"].asUUID(), + TAKE_FOCUS_NO); if ((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type) /* FIXME: && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD */) { - view->getPanel()->openSelected(); + panel->openSelected(); } // restore keyboard focus @@ -190,7 +206,8 @@ LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data, } } -LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data, +LLAssetUploadResponder::LLAssetUploadResponder( + const LLSD &post_data, const std::string& file_name, LLAssetType::EType asset_type) : LLHTTPClient::Responder(), @@ -221,14 +238,14 @@ void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason) args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = "Error in upload request. Please visit " "http://secondlife.com/support for help fixing this problem."; - LLNotifications::instance().add("CannotUploadReason", args); + LLNotificationsUtil::add("CannotUploadReason", args); break; case 500: default: args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = "The server is experiencing unexpected " "difficulties."; - LLNotifications::instance().add("CannotUploadReason", args); + LLNotificationsUtil::add("CannotUploadReason", args); break; } LLUploadDialog::modalUploadFinished(); @@ -291,7 +308,7 @@ void LLAssetUploadResponder::uploadFailure(const LLSD& content) LLSD args; args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = content["message"].asString(); - LLNotifications::instance().add("CannotUploadReason", args); + LLNotificationsUtil::add("CannotUploadReason", args); } } @@ -299,17 +316,19 @@ void LLAssetUploadResponder::uploadComplete(const LLSD& content) { } -LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, vfile_id, asset_type) +LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( + const LLSD& post_data, + const LLUUID& vfile_id, + LLAssetType::EType asset_type) + : LLAssetUploadResponder(post_data, vfile_id, asset_type) { } -LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, file_name, asset_type) +LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( + const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type) + : LLAssetUploadResponder(post_data, file_name, asset_type) { } @@ -347,17 +366,21 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) asset_type == LLAssetType::AT_ANIMATION || asset_type == LLAssetType::AT_MESH) { - expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + expected_upload_cost = + LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); } llinfos << "Adding " << content["new_inventory_item"].asUUID() << " " << content["new_asset"].asUUID() << " to inventory." << llendl; - on_new_single_inventory_upload_complete(asset_type, inventory_type, - mPostData["asset_type"].asString(), - mPostData["folder_id"].asUUID(), - mPostData["name"], - mPostData["description"], - content, expected_upload_cost); + on_new_single_inventory_upload_complete( + asset_type, + inventory_type, + mPostData["asset_type"].asString(), + mPostData["folder_id"].asUUID(), + mPostData["name"], + mPostData["description"], + content, + expected_upload_cost); // continue uploading for bulk uploads @@ -378,42 +401,54 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) // and use them for each next file to be uploaded. Note the requested // perms are not the same as the granted ones found in the given // "content" structure but can still be found in mPostData. -MG - U32 everyone_perms = mPostData.has("everyone_mask") ? - mPostData.get("everyone_mask").asInteger() : - PERM_NONE; + U32 everyone_perms = + mPostData.has("everyone_mask") ? + mPostData.get("everyone_mask").asInteger() : + PERM_NONE; - U32 group_perms = mPostData.has("group_mask") ? - mPostData.get("group_mask").asInteger() : - PERM_NONE; + U32 group_perms = + mPostData.has("group_mask") ? + mPostData.get("group_mask").asInteger() : + PERM_NONE; - U32 next_owner_perms = mPostData.has("next_owner_mask") ? - mPostData.get("next_owner_mask").asInteger() : - PERM_NONE; + U32 next_owner_perms = + mPostData.has("next_owner_mask") ? + mPostData.get("next_owner_mask").asInteger() : + PERM_NONE; std::string display_name = LLStringUtil::null; LLAssetStorage::LLStoreAssetCallback callback = NULL; void *userdata = NULL; - upload_new_resource(next_file, asset_name, asset_name, 0, - LLFolderType::FT_NONE, LLInventoryType::IT_NONE, - next_owner_perms, group_perms, everyone_perms, - display_name, callback, expected_upload_cost, - userdata); + + upload_new_resource( + next_file, + asset_name, + asset_name, + 0, + LLFolderType::FT_NONE, + LLInventoryType::IT_NONE, + next_owner_perms, + group_perms, + everyone_perms, + display_name, + callback, + expected_upload_cost, + userdata); } } LLSendTexLayerResponder::LLSendTexLayerResponder(const LLSD& post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type, - LLBakedUploadData * baked_upload_data) -: LLAssetUploadResponder(post_data, vfile_id, asset_type), + LLBakedUploadData * baked_upload_data) : + LLAssetUploadResponder(post_data, vfile_id, asset_type), mBakedUploadData(baked_upload_data) { } LLSendTexLayerResponder::~LLSendTexLayerResponder() { - // mBakedUploadData is normally deleted by calls to - // LLTexLayerSetBuffer::onTextureUploadComplete() below + // mBakedUploadData is normally deleted by calls to LLTexLayerSetBuffer::onTextureUploadComplete() below if (mBakedUploadData) { // ...but delete it in the case where uploadComplete() is never called delete mBakedUploadData; @@ -430,19 +465,16 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content) std::string result = content["state"]; LLUUID new_id = content["new_asset"]; - llinfos << "LLSendTexLayerResponder::result from capabilities: " << result << llendl; - if (result == "complete" && mBakedUploadData != NULL) + llinfos << "result: " << result << " new_id: " << new_id << llendl; + if (result == "complete" + && mBakedUploadData != NULL) { // Invoke - LLTexLayerSetBuffer::onTextureUploadComplete(new_id, - (void*)mBakedUploadData, - 0, LL_EXSTAT_NONE); + LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, 0, LL_EXSTAT_NONE); mBakedUploadData = NULL; // deleted in onTextureUploadComplete() } else { // Invoke the original callback with an error result - LLTexLayerSetBuffer::onTextureUploadComplete(new_id, - (void*)mBakedUploadData, - -1, LL_EXSTAT_NONE); + LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, -1, LL_EXSTAT_NONE); mBakedUploadData = NULL; // deleted in onTextureUploadComplete() } } @@ -452,23 +484,23 @@ void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason) llinfos << "status: " << statusNum << " reason: " << reason << llendl; // Invoke the original callback with an error result - LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), - (void*)mBakedUploadData, - -1, LL_EXSTAT_NONE); + LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE); mBakedUploadData = NULL; // deleted in onTextureUploadComplete() } -LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, vfile_id, asset_type) +LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( + const LLSD& post_data, + const LLUUID& vfile_id, + LLAssetType::EType asset_type) + : LLAssetUploadResponder(post_data, vfile_id, asset_type) { } -LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, file_name, asset_type) +LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( + const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type) + : LLAssetUploadResponder(post_data, file_name, asset_type) { } @@ -501,8 +533,7 @@ void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) case LLInventoryType::IT_NOTECARD: { // Update the UI with the new asset. - LLPreviewNotecard* nc; - nc = (LLPreviewNotecard*)LLPreview::find(new_item->getUUID()); + LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::find(new_item->getUUID()); if (nc) { // *HACK: we have to delete the asset in the VFS so @@ -511,10 +542,9 @@ void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) // the uploader, so this can be optimized away in some // cases. A better design is to have a new uuid if the // script actually changed the asset. - if (nc->hasEmbeddedInventory()) + if(nc->hasEmbeddedInventory()) { - gVFS->removeFile(content["new_asset"].asUUID(), - LLAssetType::AT_NOTECARD); + gVFS->removeFile(content["new_asset"].asUUID(), LLAssetType::AT_NOTECARD); } nc->refreshFromInventory(); } @@ -538,15 +568,16 @@ void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) } break; } + case LLInventoryType::IT_GESTURE: { // If this gesture is active, then we need to update the in-memory - // active map with the new pointer. - if (LLGestureMgr::getInstance()->isGestureActive(item_id)) - { - LLUUID asset_id = new_item->getAssetUUID(); - LLGestureMgr::getInstance()->replaceGesture(item_id, asset_id); - gInventory.notifyObservers(); + // active map with the new pointer. + if (LLGestureMgr::instance().isGestureActive(item_id)) + { + LLUUID asset_id = new_item->getAssetUUID(); + LLGestureMgr::instance().replaceGesture(item_id, asset_id); + gInventory.notifyObservers(); } //gesture will have a new asset_id @@ -639,9 +670,7 @@ void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content) // Bytecode save completed if (content["compiled"]) { - preview->callbackLSLCompileSucceeded(task_id, - item_id, - mPostData["is_script_running"]); + preview->callbackLSLCompileSucceeded(task_id, item_id, mPostData["is_script_running"]); } else { @@ -662,27 +691,30 @@ void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content) class LLNewAgentInventoryVariablePriceResponder::Impl { public: - Impl(const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_data) - : mVFileID(vfile_id), + Impl( + const LLUUID& vfile_id, + LLAssetType::EType asset_type, + const LLSD& inventory_data) : + mVFileID(vfile_id), mAssetType(asset_type), mInventoryData(inventory_data), mFileName("") { if (!gVFS->getExists(vfile_id, asset_type)) { - llwarns << "LLAssetUploadResponder called with nonexistant " - << "vfile_id " << vfile_id << llendl; + llwarns + << "LLAssetUploadResponder called with nonexistant " + << "vfile_id " << vfile_id << llendl; mVFileID.setNull(); mAssetType = LLAssetType::AT_NONE; } } - Impl(const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_data) - : mFileName(file_name), + Impl( + const std::string& file_name, + LLAssetType::EType asset_type, + const LLSD& inventory_data) : + mFileName(file_name), mAssetType(asset_type), mInventoryData(inventory_data) { @@ -741,7 +773,8 @@ public: args["FILE"] = getFilenameOrIDString(); args["REASON"] = reason; - LLNotifications::instance().add("CannotUploadReason", args); + + LLNotificationsUtil::add("CannotUploadReason", args); LLUploadDialog::modalUploadFinished(); } @@ -776,45 +809,55 @@ public: else if (_MISSING_REQUIRED_PARAMETER == error_identifier) { // Missing parameters - if (error.has(_MISSING_PARAMETER)) + if (error.has(_MISSING_PARAMETER) ) { - std::string message = "Upload request was missing required parameter '[P]'"; - LLStringUtil::replaceString(message, "[P]", - error[_MISSING_PARAMETER].asString()); + std::string message = + "Upload request was missing required parameter '[P]'"; + LLStringUtil::replaceString( + message, + "[P]", + error[_MISSING_PARAMETER].asString()); displayCannotUploadReason(message); } else { - std::string message = "Upload request was missing a required parameter"; + std::string message = + "Upload request was missing a required parameter"; displayCannotUploadReason(message); } } - else if (_INVALID_REQUEST_BODY == error_identifier) + else if ( _INVALID_REQUEST_BODY == error_identifier ) { // Invalid request body, check to see if // a particular parameter was invalid - if (error.has(_INVALID_PARAMETER)) + if ( error.has(_INVALID_PARAMETER) ) { std::string message = "Upload parameter '[P]' is invalid."; - LLStringUtil::replaceString(message, "[P]", - error[_INVALID_PARAMETER].asString()); + LLStringUtil::replaceString( + message, + "[P]", + error[_INVALID_PARAMETER].asString()); // See if the server also responds with what resource // is missing. - if (error.has(_MISSING_RESOURCE)) + if ( error.has(_MISSING_RESOURCE) ) { message += "\nMissing resource '[R]'."; - LLStringUtil::replaceString(message, "[R]", - error[_MISSING_RESOURCE].asString()); + LLStringUtil::replaceString( + message, + "[R]", + error[_MISSING_RESOURCE].asString()); } - else if (error.has(_INVALID_RESOURCE)) + else if ( error.has(_INVALID_RESOURCE) ) { message += "\nInvalid resource '[R]'."; - LLStringUtil::replaceString(message, "[R]", - error[_INVALID_RESOURCE].asString()); + LLStringUtil::replaceString( + message, + "[R]", + error[_INVALID_RESOURCE].asString()); } displayCannotUploadReason(message); @@ -837,7 +880,8 @@ public: void onTransportError() { - displayCannotUploadReason("The server is experiencing unexpected difficulties."); + displayCannotUploadReason( + "The server is experiencing unexpected difficulties."); } void onTransportError(const LLSD& error) @@ -852,47 +896,72 @@ public: // TODO*: Pull the user visible strings from an xml file // to be localized - if (_SERVER_ERROR_AFTER_CHARGE == error_identifier) + if ( _SERVER_ERROR_AFTER_CHARGE == error_identifier ) { - displayCannotUploadReason("The server is experiencing unexpected difficulties. You may have been charged for the upload."); + displayCannotUploadReason( + "The server is experiencing unexpected difficulties. You may have been charged for the upload."); } else { - displayCannotUploadReason("The server is experiencing unexpected difficulties."); + displayCannotUploadReason( + "The server is experiencing unexpected difficulties."); } } - bool uploadConfirmationCallback(const LLSD& notification, - const LLSD& response, - boost::intrusive_ptr responder) + bool uploadConfirmationCallback( + const LLSD& notification, + const LLSD& response, + boost::intrusive_ptr responder) { - S32 option = LLNotification::getSelectedOption(notification, response); - std::string confirmation_url = notification["payload"]["confirmation_url"].asString(); + S32 option; + std::string confirmation_url; + + option = LLNotificationsUtil::getSelectedOption( + notification, + response); + + confirmation_url = + notification["payload"]["confirmation_url"].asString(); // Yay! We are confirming or cancelling our upload - if (option == 0) + switch(option) { - confirmUpload(confirmation_url, responder); + case 0: + { + confirmUpload(confirmation_url, responder); + } + break; + case 1: + default: + break; } return false; } - void confirmUpload(const std::string& confirmation_url, - boost::intrusive_ptr responder) + void confirmUpload( + const std::string& confirmation_url, + boost::intrusive_ptr responder) { - if (getFilename().empty()) + if ( getFilename().empty() ) { // we have no filename, use virtual file ID instead - LLHTTPClient::postFile(confirmation_url, getVFileID(), - getAssetType(), responder); + LLHTTPClient::postFile( + confirmation_url, + getVFileID(), + getAssetType(), + responder); } else { - LLHTTPClient::postFile(confirmation_url, getFilename(), responder); + LLHTTPClient::postFile( + confirmation_url, + getFilename(), + responder); } } + private: std::string mFileName; @@ -904,18 +973,26 @@ private: /////////////////////////////////////////////// // LLNewAgentInventoryVariablePriceResponder // /////////////////////////////////////////////// -LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_info) +LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( + const LLUUID& vfile_id, + LLAssetType::EType asset_type, + const LLSD& inventory_info) { - mImpl = new Impl(vfile_id, asset_type, inventory_info); + mImpl = new Impl( + vfile_id, + asset_type, + inventory_info); } -LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_info) +LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( + const std::string& file_name, + LLAssetType::EType asset_type, + const LLSD& inventory_info) { - mImpl = new Impl(file_name, asset_type, inventory_info); + mImpl = new Impl( + file_name, + asset_type, + inventory_info); } LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResponder() @@ -923,16 +1000,19 @@ LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResp delete mImpl; } -void LLNewAgentInventoryVariablePriceResponder::errorWithContent(U32 statusNum, - const std::string& reason, - const LLSD& content) +void LLNewAgentInventoryVariablePriceResponder::errorWithContent( + U32 statusNum, + const std::string& reason, + const LLSD& content) { - LL_DEBUGS("Upload") << "LLNewAgentInventoryVariablePrice::error " - << statusNum << " reason: " << reason << LL_ENDL; + lldebugs + << "LLNewAgentInventoryVariablePrice::error " << statusNum + << " reason: " << reason << llendl; - if (content.has("error")) + if ( content.has("error") ) { static const std::string _ERROR = "error"; + mImpl->onTransportError(content[_ERROR]); } else @@ -972,27 +1052,32 @@ void LLNewAgentInventoryVariablePriceResponder::result(const LLSD& content) { // rename the file in the VFS to the actual asset id // llinfos << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << llendl; - gVFS->renameFile(mImpl->getVFileID(), asset_type, - content["new_asset"].asUUID(), asset_type); + gVFS->renameFile( + mImpl->getVFileID(), + asset_type, + content["new_asset"].asUUID(), + asset_type); } - on_new_single_inventory_upload_complete(asset_type, - mImpl->getInventoryType(), - mImpl->getInventoryTypeString(), - mImpl->getFolderID(), - mImpl->getItemName(), - mImpl->getItemDescription(), - content, - content[_UPLOAD_PRICE].asInteger()); + on_new_single_inventory_upload_complete( + asset_type, + mImpl->getInventoryType(), + mImpl->getInventoryTypeString(), + mImpl->getFolderID(), + mImpl->getItemName(), + mImpl->getItemDescription(), + content, + content[_UPLOAD_PRICE].asInteger()); // TODO* Add bulk (serial) uploading or add // a super class of this that does so } - else if (_CONFIRM_UPLOAD == state) + else if ( _CONFIRM_UPLOAD == state ) { - showConfirmationDialog(content[_UPLOAD_PRICE].asInteger(), - content[_RESOURCE_COST].asInteger(), - content[_RSVP].asString()); + showConfirmationDialog( + content[_UPLOAD_PRICE].asInteger(), + content[_RESOURCE_COST].asInteger(), + content[_RSVP].asString()); } else { @@ -1000,14 +1085,16 @@ void LLNewAgentInventoryVariablePriceResponder::result(const LLSD& content) } } -void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError(const LLSD& error) +void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError( + const LLSD& error) { mImpl->onApplicationLevelError(error); } -void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog(S32 upload_price, - S32 resource_cost, - const std::string& confirmation_url) +void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog( + S32 upload_price, + S32 resource_cost, + const std::string& confirmation_url) { if (0 == upload_price) { @@ -1027,8 +1114,9 @@ void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog(S32 uploa // when using plain ol' 'this', that this object // would be deleted before the callback is triggered // and cause sadness. - mImpl->confirmUpload(confirmation_url, - boost::intrusive_ptr(this)); + mImpl->confirmUpload( + confirmation_url, + boost::intrusive_ptr(this)); } else { @@ -1052,10 +1140,15 @@ void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog(S32 uploa // when using plain ol' 'this', that this object // would be deleted before the callback is triggered // and cause sadness. - LLNotifications::instance().add("UploadCostConfirmation", - substitutions, payload, - boost::bind(&LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback, - mImpl, _1, _2, - boost::intrusive_ptr(this))); + LLNotificationsUtil::add( + "UploadCostConfirmation", + substitutions, + payload, + boost::bind( + &LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback, + mImpl, + _1, + _2, + boost::intrusive_ptr(this))); } } diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h index 2643bc52a..0236b08b5 100644 --- a/indra/newview/llassetuploadresponders.h +++ b/indra/newview/llassetuploadresponders.h @@ -73,12 +73,14 @@ protected: class LLNewAgentInventoryResponder : public LLAssetUploadResponder { public: - LLNewAgentInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type); - LLNewAgentInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type); + LLNewAgentInventoryResponder( + const LLSD& post_data, + const LLUUID& vfile_id, + LLAssetType::EType asset_type); + LLNewAgentInventoryResponder( + const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type); virtual void error(U32 statusNum, const std::string& reason); virtual void uploadComplete(const LLSD& content); virtual void uploadFailure(const LLSD& content); @@ -92,23 +94,28 @@ class LLNewAgentInventoryVariablePriceResponder : public LLHTTPClient::Responder { public: - LLNewAgentInventoryVariablePriceResponder(const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_info); + LLNewAgentInventoryVariablePriceResponder( + const LLUUID& vfile_id, + LLAssetType::EType asset_type, + const LLSD& inventory_info); - LLNewAgentInventoryVariablePriceResponder(const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_info); + LLNewAgentInventoryVariablePriceResponder( + const std::string& file_name, + LLAssetType::EType asset_type, + const LLSD& inventory_info); virtual ~LLNewAgentInventoryVariablePriceResponder(); - void errorWithContent(U32 statusNum, - const std::string& reason, - const LLSD& content); + void errorWithContent( + U32 statusNum, + const std::string& reason, + const LLSD& content); void result(const LLSD& content); - virtual void onApplicationLevelError(const LLSD& error); - virtual void showConfirmationDialog(S32 upload_price, - S32 resource_cost, + virtual void onApplicationLevelError( + const LLSD& error); + virtual void showConfirmationDialog( + S32 upload_price, + S32 resource_cost, const std::string& confirmation_url); private: @@ -116,7 +123,7 @@ private: Impl* mImpl; }; -class LLBakedUploadData; +struct LLBakedUploadData; class LLSendTexLayerResponder : public LLAssetUploadResponder { LOG_CLASS(LLSendTexLayerResponder); diff --git a/indra/newview/llattachmentsmgr.h b/indra/newview/llattachmentsmgr.h index 768487d29..997db59d2 100644 --- a/indra/newview/llattachmentsmgr.h +++ b/indra/newview/llattachmentsmgr.h @@ -28,7 +28,7 @@ #ifndef LL_LLATTACHMENTSMGR_H #define LL_LLATTACHMENTSMGR_H -#include "llmemory.h" +#include "llsingleton.h" class LLViewerInventoryItem; diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index 4d3ce23a4..aa7d391a1 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -131,9 +131,6 @@ void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avat void LLAvatarPropertiesProcessor::sendAvatarPicksRequest(const LLUUID& avatar_id) { - std::string name; - gCacheName->getFullName(avatar_id, name); - llinfos << "Sending avatarpicksrequest for " << avatar_id << " ("<getUUID(_PREHASH_AgentData, _PREHASH_AgentID, avatar_picks.agent_id); msg->getUUID(_PREHASH_AgentData, _PREHASH_TargetID, avatar_picks.target_id); - std::string name; - gCacheName->getFullName(avatar_picks.target_id, name); - - llinfos << "Got reply for " << avatar_picks.target_id << ": (" << name << ")" << llendl; S32 block_count = msg->getNumberOfBlocks(_PREHASH_Data); for (int block = 0; block < block_count; ++block) { @@ -411,14 +404,12 @@ void LLAvatarPropertiesProcessor::processAvatarPicksReply(LLMessageSystem* msg, msg->getUUID(_PREHASH_Data, _PREHASH_PickID, pick_id, block); msg->getString(_PREHASH_Data, _PREHASH_PickName, pick_name, block); - llinfos << "\t" << pick_id << ": " << pick_name << llendl; avatar_picks.picks_list.push_back(std::make_pair(pick_id,pick_name)); } LLAvatarPropertiesProcessor* self = getInstance(); // Request processed, no longer pending self->removePendingRequest(avatar_picks.target_id, APT_PICKS); self->notifyObservers(avatar_picks.target_id,&avatar_picks,APT_PICKS); - //LLPanelAvatar } void LLAvatarPropertiesProcessor::processPickInfoReply(LLMessageSystem* msg, void**) diff --git a/indra/newview/llbuildnewviewsscheduler.cpp b/indra/newview/llbuildnewviewsscheduler.cpp index 964155b58..16d2900ed 100644 --- a/indra/newview/llbuildnewviewsscheduler.cpp +++ b/indra/newview/llbuildnewviewsscheduler.cpp @@ -65,12 +65,15 @@ void LLBuildNewViewsScheduler::buildNewViews(LLInventoryPanel* panelp, LLInvento objectp->getType(), LLInventoryType::IT_CATEGORY, panelp, + panelp->getRootFolder(), objectp->getUUID()); if (new_listener) { LLFolderViewFolder* folderp = new LLFolderViewFolder(new_listener->getDisplayName(), new_listener->getIcon(), + new_listener->getIcon(), + NULL, panelp->getRootFolder(), new_listener); @@ -86,12 +89,15 @@ void LLBuildNewViewsScheduler::buildNewViews(LLInventoryPanel* panelp, LLInvento item->getActualType(), item->getInventoryType(), panelp, + panelp->getRootFolder(), item->getUUID(), item->getFlags()); if (new_listener) { itemp = new LLFolderViewItem(new_listener->getDisplayName(), new_listener->getIcon(), + NULL, + NULL, new_listener->getCreationDate(), panelp->getRootFolder(), new_listener); @@ -102,7 +108,7 @@ void LLBuildNewViewsScheduler::buildNewViews(LLInventoryPanel* panelp, LLInvento if (itemp) { - itemp->mDelayedDelete = TRUE; + //itemp->mDelayedDelete = TRUE; if (parent_folder) { itemp->addToFolder(parent_folder, panelp->getRootFolder()); diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp index d59394083..1bfd2cb9b 100644 --- a/indra/newview/llcallbacklist.cpp +++ b/indra/newview/llcallbacklist.cpp @@ -180,6 +180,12 @@ private: bool_func_t mCallable; }; +void doOnIdleRepeating(bool_func_t callable) +{ + OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable); + gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor); +} + #ifdef _DEBUG void test1(void *data) diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index ef1413954..b91ad1f4d 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -153,8 +153,8 @@ BOOL LLChatBar::postBuild() { mInputEditor->setCallbackUserData(this); mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke); - mInputEditor->setFocusLostCallback(&onInputEditorFocusLost, this); - mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus, this ); + mInputEditor->setFocusLostCallback(boost::bind(&LLChatBar::onInputEditorFocusLost)); + mInputEditor->setFocusReceivedCallback(boost::bind(&LLChatBar::onInputEditorGainFocus)); mInputEditor->setCommitOnFocusLost( FALSE ); mInputEditor->setRevertOnEsc( FALSE ); mInputEditor->setIgnoreTab(TRUE); @@ -623,14 +623,14 @@ void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata ) } // static -void LLChatBar::onInputEditorFocusLost( LLFocusableElement* caller, void* userdata) +void LLChatBar::onInputEditorFocusLost() { // stop typing animation gAgent.stopTyping(); } // static -void LLChatBar::onInputEditorGainFocus( LLFocusableElement* caller, void* userdata ) +void LLChatBar::onInputEditorGainFocus() { LLFloaterChat::setHistoryCursorAndScrollToEnd(); } diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h index 56a98a78f..3fa826ab9 100644 --- a/indra/newview/llchatbar.h +++ b/indra/newview/llchatbar.h @@ -86,8 +86,8 @@ public: static void onTabClick( void* userdata ); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - static void onInputEditorFocusLost(LLFocusableElement* caller,void* userdata); - static void onInputEditorGainFocus(LLFocusableElement* caller,void* userdata); + static void onInputEditorFocusLost(); + static void onInputEditorGainFocus(); static void onCommitGesture(LLUICtrl* ctrl, void* data); diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index 2ba89a29a..8b349c9cc 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -218,7 +218,7 @@ void LLColorSwatchCtrl::draw() if ( mValid ) { // Draw the color swatch - gl_rect_2d_checkerboard( getScreenRect(), interior ); + gl_rect_2d_checkerboard( calcScreenRect(), interior ); gl_rect_2d(interior, mColor, TRUE); LLColor4 opaque_color = mColor; opaque_color.mV[VALPHA] = 1.f; @@ -239,7 +239,7 @@ void LLColorSwatchCtrl::draw() LLPointer fallback_image = LLViewerTextureManager::getFetchedTextureFromFile(mFallbackImageName); if( fallback_image->getComponents() == 4 ) { - gl_rect_2d_checkerboard( getScreenRect(), interior ); + gl_rect_2d_checkerboard( calcScreenRect(), interior ); } gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), fallback_image); fallback_image->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); diff --git a/indra/newview/lldriverparam.cpp b/indra/newview/lldriverparam.cpp index fb220d24a..8332a6221 100644 --- a/indra/newview/lldriverparam.cpp +++ b/indra/newview/lldriverparam.cpp @@ -2,31 +2,25 @@ * @file lldriverparam.cpp * @brief A visual parameter that drives (controls) other visual parameters. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,6 +30,10 @@ #include "llfasttimer.h" #include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llagent.h" +#include "llwearable.h" +#include "llagentwearables.h" //----------------------------------------------------------------------------- // LLDriverParamInfo @@ -100,12 +98,69 @@ BOOL LLDriverParamInfo::parseXml(LLXmlTreeNode* node) return TRUE; } +//virtual +void LLDriverParamInfo::toStream(std::ostream &out) +{ + LLViewerVisualParamInfo::toStream(out); + out << "driver" << "\t"; + out << mDrivenInfoList.size() << "\t"; + for (entry_info_list_t::iterator iter = mDrivenInfoList.begin(); iter != mDrivenInfoList.end(); iter++) + { + LLDrivenEntryInfo driven = *iter; + out << driven.mDrivenID << "\t"; + } + + out << std::endl; + + if(isAgentAvatarValid()) + { + for (entry_info_list_t::iterator iter = mDrivenInfoList.begin(); iter != mDrivenInfoList.end(); iter++) + { + LLDrivenEntryInfo driven = *iter; + LLViewerVisualParam *param = (LLViewerVisualParam*)gAgentAvatarp->getVisualParam(driven.mDrivenID); + if (param) + { + param->getInfo()->toStream(out); + if (param->getWearableType() != mWearableType) + { + if(param->getCrossWearable()) + { + out << "cross-wearable" << "\t"; + } + else + { + out << "ERROR!" << "\t"; + } + } + else + { + out << "valid" << "\t"; + } + } + else + { + llwarns << "could not get parameter " << driven.mDrivenID << " from avatar " << gAgentAvatarp << " for driver parameter " << getID() << llendl; + } + out << std::endl; + } + } +} + //----------------------------------------------------------------------------- // LLDriverParam //----------------------------------------------------------------------------- -LLDriverParam::LLDriverParam(LLVOAvatar *avatarp) - : mCurrentDistortionParam( NULL ), mAvatarp(avatarp) +LLDriverParam::LLDriverParam(LLVOAvatar *avatarp) : + mCurrentDistortionParam( NULL ), + mAvatarp(avatarp), + mWearablep(NULL) +{ +} + +LLDriverParam::LLDriverParam(LLWearable *wearablep) : + mCurrentDistortionParam( NULL ), + mAvatarp(NULL), + mWearablep(wearablep) { } @@ -122,27 +177,48 @@ BOOL LLDriverParam::setInfo(LLDriverParamInfo *info) mID = info->mID; setWeight(getDefaultWeight(), FALSE ); - - LLDriverParamInfo::entry_info_list_t::iterator iter; - mDriven.reserve(getInfo()->mDrivenInfoList.size()); - for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); iter++) + + return TRUE; +} + +void LLDriverParam::setWearable(LLWearable *wearablep) +{ + if (wearablep) { - LLDrivenEntryInfo *driven_info = &(*iter); - S32 driven_id = driven_info->mDrivenID; - LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarp->getVisualParam( driven_id ); - if (param) + mWearablep = wearablep; + mAvatarp = NULL; + } +} + +void LLDriverParam::setAvatar(LLVOAvatar *avatarp) +{ + if (avatarp) + { + mWearablep = NULL; + mAvatarp = avatarp; + } +} + +/*virtual*/ LLViewerVisualParam* LLDriverParam::cloneParam(LLWearable* wearable) const +{ + LLDriverParam *new_param; + if (wearable) + { + new_param = new LLDriverParam(wearable); + } + else + { + if (mWearablep) { - mDriven.push_back(LLDrivenEntry( param, driven_info )); + new_param = new LLDriverParam(mWearablep); } else { - llerrs << " Unable to resolve driven parameter: " << driven_id << llendl; - mInfo = NULL; - return FALSE; + new_param = new LLDriverParam(mAvatarp); } } - - return TRUE; + *new_param = *this; + return new_param; } #if 0 // obsolete @@ -408,6 +484,82 @@ void LLDriverParam::stopAnimating(BOOL upload_bake) } } +/*virtual*/ +BOOL LLDriverParam::linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params) +{ + BOOL success = TRUE; + LLDriverParamInfo::entry_info_list_t::iterator iter; + for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); ++iter) + { + LLDrivenEntryInfo *driven_info = &(*iter); + S32 driven_id = driven_info->mDrivenID; + + // check for already existing links. Do not overwrite. + BOOL found = FALSE; + for (entry_list_t::iterator driven_iter = mDriven.begin(); driven_iter != mDriven.end() && !found; ++driven_iter) + { + if (driven_iter->mInfo->mDrivenID == driven_id) + { + found = TRUE; + } + } + + if (!found) + { + LLViewerVisualParam* param = (LLViewerVisualParam*)mapper(driven_id); + bool push = param && (!only_cross_params || param->getCrossWearable()); + if (push) + { + mDriven.push_back(LLDrivenEntry( param, driven_info )); + } + else + { + success = FALSE; + } + } + } + + return success; +} + +void LLDriverParam::resetDrivenParams() +{ + mDriven.clear(); + mDriven.reserve(getInfo()->mDrivenInfoList.size()); +} + +void LLDriverParam::updateCrossDrivenParams(LLWearableType::EType driven_type) +{ + bool needs_update = (getWearableType()==driven_type); + + // if the driver has a driven entry for the passed-in wearable type, we need to refresh the value + for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ ) + { + LLDrivenEntry* driven = &(*iter); + if (driven && driven->mParam && driven->mParam->getCrossWearable() && driven->mParam->getWearableType() == driven_type) + { + needs_update = true; + } + } + + + if (needs_update) + { + LLWearableType::EType driver_type = (LLWearableType::EType)getWearableType(); + + // If we've gotten here, we've added a new wearable of type "type" + // Thus this wearable needs to get updates from the driver wearable. + // The call to setVisualParamWeight seems redundant, but is necessary + // as the number of driven wearables has changed since the last update. -Nyx + LLWearable *wearable = gAgentWearables.getTopWearable(driver_type); + if (wearable) + { + wearable->setVisualParamWeight(mID, wearable->getVisualParamWeight(mID), false); + } + } +} + + //----------------------------------------------------------------------------- // getDrivenWeight() //----------------------------------------------------------------------------- @@ -467,7 +619,7 @@ F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake) { - /*if(isAgentAvatarValid() && + if(isAgentAvatarValid() && mWearablep && driven->mParam->getCrossWearable() && mWearablep->isOnTop()) @@ -475,7 +627,7 @@ void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bo // call setWeight through LLVOAvatarSelf so other wearables can be updated with the correct values gAgentAvatarp->setVisualParamWeight( (LLVisualParam*)driven->mParam, driven_weight, upload_bake ); } - else*/ + else { driven->mParam->setWeight( driven_weight, upload_bake ); } diff --git a/indra/newview/lldriverparam.h b/indra/newview/lldriverparam.h index f881e445e..fb1b44458 100644 --- a/indra/newview/lldriverparam.h +++ b/indra/newview/lldriverparam.h @@ -2,31 +2,25 @@ * @file lldriverparam.h * @brief A visual parameter that drives (controls) other visual parameters. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,9 +28,11 @@ #define LL_LLDRIVERPARAM_H #include "llviewervisualparam.h" +#include "llwearabletype.h" class LLPhysicsMotion; class LLVOAvatar; +class LLWearable; //----------------------------------------------------------------------------- @@ -70,6 +66,8 @@ public: /*virtual*/ BOOL parseXml(LLXmlTreeNode* node); + /*virtual*/ void toStream(std::ostream &out); + protected: typedef std::deque entry_info_list_t; entry_info_list_t mDrivenInfoList; @@ -82,6 +80,7 @@ class LLDriverParam : public LLViewerVisualParam friend class LLPhysicsMotion; // physics motion needs to access driven params directly. public: LLDriverParam(LLVOAvatar *avatarp); + LLDriverParam(LLWearable *wearablep); ~LLDriverParam(); // Special: These functions are overridden by child classes @@ -89,12 +88,20 @@ public: // This sets mInfo and calls initialization functions BOOL setInfo(LLDriverParamInfo *info); + void setWearable(LLWearable *wearablep); + void setAvatar(LLVOAvatar *avatarp); + void updateCrossDrivenParams(LLWearableType::EType driven_type); + + /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const; + // LLVisualParam Virtual functions ///*virtual*/ BOOL parseData(LLXmlTreeNode* node); /*virtual*/ void apply( ESex sex ) {} // apply is called separately for each driven param. /*virtual*/ void setWeight(F32 weight, BOOL upload_bake); /*virtual*/ void setAnimationTarget( F32 target_value, BOOL upload_bake ); /*virtual*/ void stopAnimating(BOOL upload_bake); + /*virtual*/ BOOL linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params); + /*virtual*/ void resetDrivenParams(); // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion(); @@ -115,6 +122,7 @@ protected: LLViewerVisualParam* mCurrentDistortionParam; // Backlink only; don't make this an LLPointer. LLVOAvatar* mAvatarp; + LLWearable* mWearablep; }; #endif // LL_LLDRIVERPARAM_H diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index c2776df5a..32a533570 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -2,31 +2,25 @@ * @file llflexibleobject.cpp * @brief Flexible object implementation * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp index 13f9d5524..95ba4a08f 100644 --- a/indra/newview/llfloateranimpreview.cpp +++ b/indra/newview/llfloateranimpreview.cpp @@ -267,12 +267,13 @@ BOOL LLFloaterAnimPreview::postBuild() { mPlayButton = new LLButton(std::string("play_btn"), LLRect(0,0,0,0)); } - mPlayButton->setClickedCallback(onBtnPlay); - mPlayButton->setCallbackUserData(this); + mPlayButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnPlay,this)); mPlayButton->setImages(std::string("button_anim_play.tga"), std::string("button_anim_play_selected.tga")); - mPlayButton->setDisabledImages(LLStringUtil::null,LLStringUtil::null); + + mPlayButton->setImageDisabled(NULL); + mPlayButton->setImageDisabledSelected(NULL); mPlayButton->setScaleImage(TRUE); @@ -281,12 +282,13 @@ BOOL LLFloaterAnimPreview::postBuild() { mStopButton = new LLButton(std::string("stop_btn"), LLRect(0,0,0,0)); } - mStopButton->setClickedCallback(onBtnStop); - mStopButton->setCallbackUserData(this); + mStopButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnStop, this)); mStopButton->setImages(std::string("button_anim_stop.tga"), std::string("button_anim_stop_selected.tga")); - mStopButton->setDisabledImages(LLStringUtil::null,LLStringUtil::null); + + mStopButton->setImageDisabled(NULL); + mStopButton->setImageDisabledSelected(NULL); mStopButton->setScaleImage(TRUE); diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index 60a6df143..9897109a7 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -236,63 +236,35 @@ LLAvatarListEntry::ACTIVITY_TYPE LLAvatarListEntry::getActivity() return mActivityType; } -LLFloaterAvatarList* LLFloaterAvatarList::sInstance = NULL; - LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")) { - llassert_always(sInstance == NULL); - sInstance = this; + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_radar.xml"); mUpdateRate = gSavedSettings.getU32("RadarUpdateRate") * 3 + 3; } LLFloaterAvatarList::~LLFloaterAvatarList() { gIdleCallbacks.deleteFunction(LLFloaterAvatarList::callbackIdle); - sInstance = NULL; -} -//static -void LLFloaterAvatarList::createInstance(bool visible) -{ - sInstance = new LLFloaterAvatarList(); - LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_radar.xml"); - if(!visible) - { - sInstance->setVisible(FALSE); - gSavedSettings.setBOOL("ShowRadar", FALSE); - } - if(gHippoGridManager->getConnectedGrid()->isSecondLife()){ - LLScrollListCtrl* list = sInstance->getChild("avatar_list"); - list->getColumn(1)->setWidth(0); - list->getColumn(6)->setWidth(0); - list->getColumn(6)->mDynamicWidth = FALSE; - list->getColumn(6)->mRelWidth = 0; - list->getColumn(1)->mDynamicWidth = TRUE; - list->getColumn(1)->mRelWidth = -1; - list->updateLayout(); - } } + //static void LLFloaterAvatarList::toggle(void*) { - if (sInstance) - { - if (sInstance->getVisible() // [RLVa:KB] - || gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES) + if(gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + if(instanceExists()) + getInstance()->close(); + } + else // [/RLVa:KB] - ) - - { - sInstance->close(false); - } - else - { - sInstance->open(); - } + if(!instanceExists() || !getInstance()->getVisible()) + { + showInstance(); } else { - showInstance(); + getInstance()->close(); } } @@ -303,17 +275,7 @@ void LLFloaterAvatarList::showInstance() if(gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) return; // [/RLVa:KB] - if (sInstance) - { - if (!sInstance->getVisible()) - { - sInstance->open(); - } - } - else - { - createInstance(true); - } + getInstance()->open(); } void LLFloaterAvatarList::draw() @@ -324,12 +286,11 @@ void LLFloaterAvatarList::draw() void LLFloaterAvatarList::onOpen() { gSavedSettings.setBOOL("ShowRadar", TRUE); - sInstance->setVisible(TRUE); } void LLFloaterAvatarList::onClose(bool app_quitting) { - sInstance->setVisible(FALSE); + setVisible(FALSE); if (!app_quitting) { gSavedSettings.setBOOL("ShowRadar", FALSE); @@ -387,12 +348,23 @@ BOOL LLFloaterAvatarList::postBuild() gIdleCallbacks.addFunction(LLFloaterAvatarList::callbackIdle); + if(gHippoGridManager->getConnectedGrid()->isSecondLife()){ + LLScrollListCtrl* list = sInstance->getChild("avatar_list"); + list->getColumn(1)->setWidth(0); + list->getColumn(6)->setWidth(0); + list->getColumn(6)->mDynamicWidth = FALSE; + list->getColumn(6)->mRelWidth = 0; + list->getColumn(1)->mDynamicWidth = TRUE; + list->getColumn(1)->mRelWidth = -1; + list->updateLayout(); + } + return TRUE; } void updateParticleActivity(LLDrawable *drawablep) { - if (LLFloaterAvatarList::getInstance()) + if (LLFloaterAvatarList::instanceExists()) { LLViewerObject *vobj = drawablep->getVObj(); if (vobj && vobj->isParticleSource()) @@ -409,10 +381,6 @@ void updateParticleActivity(LLDrawable *drawablep) void LLFloaterAvatarList::updateAvatarList() { - if (sInstance != this) return; - - //if(LLStartUp::getStartupState() < STATE_STARTED)return; - //llinfos << "radar refresh: updating map" << llendl; // Check whether updates are enabled @@ -672,7 +640,7 @@ void LLFloaterAvatarList::expireAvatarList() void LLFloaterAvatarList::refreshAvatarList() { // Don't update list when interface is hidden - if (!sInstance->getVisible()) return; + if (!getVisible()) return; // We rebuild the list fully each time it's refreshed // The assumption is that it's faster to refill it and sort than @@ -1545,15 +1513,13 @@ void LLFloaterAvatarList::callbackFreeze(const LLSD& notification, const LLSD& r { S32 option = LLNotification::getSelectedOption(notification, response); - LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance; - if (option == 0) { - self->doCommand(cmd_freeze); + getInstance()->doCommand( cmd_freeze ); } else if (option == 1) { - self->doCommand(cmd_unfreeze); + getInstance()->doCommand( cmd_unfreeze ); } } @@ -1562,15 +1528,13 @@ void LLFloaterAvatarList::callbackEject(const LLSD& notification, const LLSD& re { S32 option = LLNotification::getSelectedOption(notification, response); - LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance; - if (option == 0) { - self->doCommand(cmd_eject); + getInstance()->doCommand( cmd_eject ); } else if (option == 1) { - self->doCommand(cmd_ban); + getInstance()->doCommand( cmd_ban ); } } @@ -1579,22 +1543,21 @@ void LLFloaterAvatarList::callbackEjectFromEstate(const LLSD& notification, cons { S32 option = LLNotification::getSelectedOption(notification, response); - LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance; - if (option == 0) { - self->doCommand(cmd_estate_eject); + getInstance()->doCommand( cmd_estate_eject ); } } //static -void LLFloaterAvatarList::callbackIdle(void *userdata) { - if (LLFloaterAvatarList::sInstance != NULL) +void LLFloaterAvatarList::callbackIdle(void *userdata) +{ + if (instanceExists()) { // Do not update at every frame: this would be insane ! - if (gFrameCount % LLFloaterAvatarList::sInstance->mUpdateRate == 0) + if (gFrameCount % getInstance()->mUpdateRate == 0) { - LLFloaterAvatarList::sInstance->updateAvatarList(); + getInstance()->updateAvatarList(); } } } diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h index fa7f9b5f3..44bc3df84 100644 --- a/indra/newview/llfloateravatarlist.h +++ b/indra/newview/llfloateravatarlist.h @@ -159,12 +159,13 @@ private: * Since I'm very new to C++ any suggestions on coding, style, etc are very * welcome. */ -class LLFloaterAvatarList : public LLFloater +class LLFloaterAvatarList : public LLFloater, public LLSingleton { /** * @brief Creates and initializes the LLFloaterAvatarList * Here the interface is created, and callbacks are initialized. */ + friend class LLSingleton; private: LLFloaterAvatarList(); public: @@ -211,11 +212,6 @@ public: static void sound_trigger_hook(LLMessageSystem* msg,void **); static void sendKeys(); -private: - static LLFloaterAvatarList* sInstance; - -public: - static LLFloaterAvatarList* getInstance() { return sInstance; } private: // when a line editor loses keyboard focus, it is committed. // commit callbacks are named onCommitWidgetName by convention. diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index c3ac0cfa8..02db1cc7b 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -140,7 +140,7 @@ BOOL LLFloaterAvatarPicker::postBuild() inventory_panel->setFollowsAll(); inventory_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); inventory_panel->openDefaultFolderForType(LLAssetType::AT_CALLINGCARD); - inventory_panel->setSelectCallback(LLFloaterAvatarPicker::onCallingCardSelectionChange, this); + inventory_panel->setSelectCallback(boost::bind(&LLFloaterAvatarPicker::onCallingCardSelectionChange, _1, _2, (void*)this)); childSetTabChangeCallback("ResidentChooserTabs", "SearchPanel", onTabChanged, this); childSetTabChangeCallback("ResidentChooserTabs", "CallingCardsPanel", onTabChanged, this); diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 717922394..474e4d874 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -224,20 +224,17 @@ LLFloaterColorPicker:: postBuild() { mCancelBtn = getChild( "cancel_btn" ); - mCancelBtn->setClickedCallback ( onClickCancel ); - mCancelBtn->setCallbackUserData ( this ); + mCancelBtn->setClickedCallback ( boost::bind(&LLFloaterColorPicker::onClickCancel,this) ); mSelectBtn = getChild( "select_btn"); - mSelectBtn->setClickedCallback ( onClickSelect ); - mSelectBtn->setCallbackUserData ( this ); + mSelectBtn->setClickedCallback ( boost::bind(&LLFloaterColorPicker::onClickSelect,this) ); mSelectBtn->setFocus ( TRUE ); mPipetteBtn = getChild("color_pipette" ); mPipetteBtn->setImages(std::string("eye_button_inactive.tga"), std::string("eye_button_active.tga")); - mPipetteBtn->setClickedCallback( onClickPipette ); - mPipetteBtn->setCallbackUserData ( this ); + mPipetteBtn->setClickedCallback( boost::bind(&LLFloaterColorPicker::onClickPipette,this) ); mApplyImmediateCheck = getChild("apply_immediate"); mApplyImmediateCheck->set(gSavedSettings.getBOOL("ApplyColorImmediately")); @@ -645,6 +642,12 @@ void LLFloaterColorPicker::draw() startX + mLumMarkerSize, startY + mLumMarkerSize, LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), TRUE ); + // draw a white marker inside the black marker to be visible with dark or bright ui. + gl_triangle_2d ( startX+2, startY, + startX + mLumMarkerSize - 1, startY - mLumMarkerSize + 2, + startX + mLumMarkerSize - 1, startY + mLumMarkerSize - 2, + LLColor4 ( 1.0f, 1.0f, 1.0f, 1.0f ), TRUE ); + // draw luminance slider outline gl_rect_2d ( mLumRegionLeft, mLumRegionTop - mLumRegionHeight + 1, diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index fc72e7e17..e9ec8c6aa 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -32,6 +32,7 @@ #include "llviewerprecompiledheaders.h" +#include "llappearancemgr.h" #include "llimagejpeg.h" #include "llfloatercustomize.h" #include "llfontgl.h" @@ -420,8 +421,12 @@ enum ESubpart { struct LLSubpart { - LLSubpart() : mSex( SEX_BOTH ), mVisualHint(true) {} - + LLSubpart(const char* pJoint, const char* pGroup, const LLVector3d &target_offs, const LLVector3d &cam_offs, const ESex sex=SEX_BOTH, bool visual_hint=true ) + : mSex( sex ), mVisualHint(visual_hint), mTargetJoint(pJoint), mEditGroup(pGroup), mTargetOffset(target_offs), mCameraOffset(cam_offs) + {} + LLSubpart(const char* pJoint, const char* pGroup, const LLVector3d &target_offs, const LLVector3d &cam_offs, bool visual_hint) + : mSex( SEX_BOTH ), mVisualHint(visual_hint), mTargetJoint(pJoint), mEditGroup(pGroup), mTargetOffset(target_offs), mCameraOffset(cam_offs) + {} std::string mButtonName; std::string mTargetJoint; std::string mEditGroup; @@ -740,14 +745,11 @@ bool LLPanelEditWearable::onSelectAutoWearOption(const LLSD& notification, const bool LLPanelEditWearable::textureIsInvisible(ETextureIndex te) { - if (gAgentWearables.getWearable(mType, 0)) // TODO: MULTI-WEARABLE + const LLWearable* wearable = gAgentWearables.getWearable(mType, 0); // TODO: MULTI-WEARABLE + if(wearable) { - LLVOAvatar *avatar = gAgentAvatarp; - if (avatar) - { - const LLTextureEntry* current_te = avatar->getTE(te); - return (current_te && current_te->getID() == IMG_INVISIBLE); - } + const LLLocalTextureObject* lto = wearable->getLocalTextureObject(te); + return (lto && lto->getID() == IMG_INVISIBLE); } return false; } @@ -764,8 +766,7 @@ void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, void* userdata) { LLPanelEditWearable* self = (LLPanelEditWearable*) userdata; LLCheckBoxCtrl* checkbox_ctrl = (LLCheckBoxCtrl*) ctrl; - LLVOAvatar *avatar = gAgentAvatarp; - if (!avatar) + if (!gAgentAvatarp) { return; } @@ -775,14 +776,24 @@ void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, void* userdata) bool new_invis_state = checkbox_ctrl->get(); if (new_invis_state) { - LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(IMG_INVISIBLE); - const LLTextureEntry* current_te = avatar->getTE(te); - if (current_te) + + LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(IMG_INVISIBLE); + + LLWearable* wearable = gAgentWearables.getWearable(self->mType,0); // TODO: MULTI-WEARABLE + const LLLocalTextureObject* lto = wearable ? wearable->getLocalTextureObject(te) : NULL; + + if(lto) { - self->mPreviousTextureList[(S32)te] = current_te->getID(); + self->mPreviousTextureList[(S32)te] = lto->getID(); } - avatar->setLocTexTE(te, image, TRUE); - avatar->wearableUpdated(self->mType, FALSE); + if(wearable) + { + LLLocalTextureObject new_lto(image, IMG_INVISIBLE); + wearable->setLocalTextureObject(te, new_lto); + wearable->writeToAvatar(); + gAgentAvatarp->wearableUpdated(self->mType, FALSE); + } + } else { @@ -794,9 +805,15 @@ void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, void* userdata) } if (prev_id.notNull()) { - LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(prev_id); - avatar->setLocTexTE(te, image, TRUE); - avatar->wearableUpdated(self->mType, FALSE); + LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(prev_id); + LLWearable* wearable = gAgentWearables.getWearable(self->mType,0); // TODO: MULTI-WEARABLE + if(wearable) + { + LLLocalTextureObject new_lto(image, prev_id); + wearable->setLocalTextureObject(te, new_lto); + wearable->writeToAvatar(); + gAgentAvatarp->wearableUpdated(self->mType, FALSE); + } } } @@ -814,23 +831,26 @@ void LLPanelEditWearable::onColorCommit( LLUICtrl* ctrl, void* userdata ) LLPanelEditWearable* self = (LLPanelEditWearable*) userdata; LLColorSwatchCtrl* color_ctrl = (LLColorSwatchCtrl*) ctrl; - LLVOAvatar* avatar = gAgentAvatarp; - if( self && color_ctrl && avatar ) + if( self && color_ctrl && gAgentAvatarp ) { std::map::const_iterator cl_itr = self->mColorList.find(ctrl->getName()); if(cl_itr != self->mColorList.end()) { ETextureIndex te = (ETextureIndex)cl_itr->second; - LLColor4 old_color = avatar->getClothesColor( te ); + LLWearable* wearable = gAgentWearables.getWearable(self->mType, 0); // TODO: MULTI-WEARABLE + if(!wearable) + return; + + LLColor4 old_color = wearable->getClothesColor( te ); const LLColor4& new_color = color_ctrl->get(); if( old_color != new_color ) { // Set the new version - avatar->setClothesColor( te, new_color, TRUE ); - + wearable->setClothesColor(te, new_color, TRUE); + wearable->writeToAvatar(); + gAgentAvatarp->wearableUpdated(self->mType, FALSE); LLVisualParamHint::requestHintUpdates(); - avatar->wearableUpdated(self->mType, FALSE); } } } @@ -852,10 +872,15 @@ void LLPanelEditWearable::initPreviousTextureListEntry(ETextureIndex te) { return; } - const LLTextureEntry* current_te = avatar->getTE(te); - if (current_te) + + LLWearable* wearable = gAgentWearables.getWearable(mType, 0); // TODO: MULTI-WEARABLE + if(!wearable) + return; + + const LLLocalTextureObject* lto = wearable->getLocalTextureObject(te); + if (lto) { - mPreviousTextureList[te] = current_te->getID(); + mPreviousTextureList[te] = lto->getID(); } } @@ -879,10 +904,10 @@ void LLPanelEditWearable::addTextureDropTarget( ETextureIndex te, const std::str LLWearable* wearable = gAgentWearables.getWearable(mType, 0); // TODO: MULTI-WEARABLE if (wearable && mType == LLWearableType::WT_ALPHA) { - const LLTextureEntry* current_te = avatar->getTE(te); - if (current_te) + const LLLocalTextureObject* lto = wearable->getLocalTextureObject(te); + if (lto) { - mPreviousTextureList[te] = current_te->getID(); + mPreviousTextureList[te] = lto->getID(); } } } @@ -894,22 +919,25 @@ void LLPanelEditWearable::onTextureCommit( LLUICtrl* ctrl, void* userdata ) LLPanelEditWearable* self = (LLPanelEditWearable*) userdata; LLTextureCtrl* texture_ctrl = (LLTextureCtrl*) ctrl; - LLVOAvatar* avatar = gAgentAvatarp; - if( avatar ) + if( gAgentAvatarp ) { ETextureIndex te = (ETextureIndex)(self->mTextureList[ctrl->getName()]); // Set the new version - LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture( texture_ctrl->getImageAssetID() ); + LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture( texture_ctrl->getImageAssetID() ); if (image->getID().isNull()) { image = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR); } self->mTextureList[ctrl->getName()] = te; - if (gAgentWearables.getWearable(self->mType, 0)) // TODO: MULTI-WEARABLE + + LLWearable* wearable = gAgentWearables.getWearable(self->mType, 0); // TODO: MULTI-WEARABLE + if (wearable) { - avatar->setLocTexTE(te, image, TRUE); - avatar->wearableUpdated(self->mType, FALSE); + LLLocalTextureObject lto(image, texture_ctrl->getImageAssetID()); + wearable->setLocalTextureObject(te, lto); + wearable->writeToAvatar(); + gAgentAvatarp->wearableUpdated(self->mType, FALSE); } if (self->mType == LLWearableType::WT_ALPHA && image->getID() != IMG_INVISIBLE) { @@ -1068,13 +1096,14 @@ void LLPanelEditWearable::draw() childSetVisible(name, is_copyable && is_modifiable && is_complete ); if (texture_ctrl) { - const LLTextureEntry* te = avatar->getTE(te_index); + + const LLLocalTextureObject* lto = wearable->getLocalTextureObject(te_index); LLUUID new_id; - if( te && (te->getID() != IMG_DEFAULT_AVATAR) ) + if( lto && (lto->getID() != IMG_DEFAULT_AVATAR) ) { - new_id = te->getID(); + new_id = lto->getID(); } else { @@ -1093,17 +1122,22 @@ void LLPanelEditWearable::draw() } } - for( std::map::iterator iter = mColorList.begin(); - iter != mColorList.end(); ++iter ) + LLWearable* wearable = gAgentWearables.getWearable(mType, 0); // TODO: MULTI-WEARABLE + + if(wearable) { - std::string name = iter->first; - S32 te_index = iter->second; - childSetVisible(name, is_modifiable && is_complete ); - childSetEnabled(name, is_modifiable && is_complete ); - LLColorSwatchCtrl* ctrl = getChild(name); - if (ctrl) + for( std::map::iterator iter = mColorList.begin(); + iter != mColorList.end(); ++iter ) { - ctrl->set(avatar->getClothesColor( (ETextureIndex)te_index ) ); + std::string name = iter->first; + S32 te_index = iter->second; + childSetVisible(name, is_modifiable && is_complete ); + childSetEnabled(name, is_modifiable && is_complete ); + LLColorSwatchCtrl* ctrl = getChild(name); + if (ctrl) + { + ctrl->set( wearable->getClothesColor(te_index ) ); + } } } @@ -1231,19 +1265,29 @@ void LLPanelEditWearable::onCommitSexChange( LLUICtrl*, void* userdata ) return; } + LLWearable* wearable = gAgentWearables.getWearable(self->mType,0); // TODO: MULTI-WEARABLE + if(!wearable) + { + return; + } + ESex new_sex = gSavedSettings.getU32("AvatarSex") ? SEX_MALE : SEX_FEMALE; - LLViewerVisualParam* param = (LLViewerVisualParam*)avatar->getVisualParam( "male" ); + LLVisualParam* param = avatar->getVisualParam( "male" ); if( !param ) { return; } - param->setWeight( (new_sex == SEX_MALE), TRUE ); + wearable->setVisualParamWeight(param->getID(), (new_sex == SEX_MALE), TRUE); + wearable->writeToAvatar(); + avatar->updateVisualParams(); + + /*param->setWeight( (new_sex == SEX_MALE), TRUE ); avatar->updateSexDependentLayerSets( TRUE ); - avatar->updateVisualParams(); + avatar->updateVisualParams();*/ gFloaterCustomize->clearScrollingPanelList(); @@ -1375,15 +1419,15 @@ LLScrollingPanelParam::LLScrollingPanelParam( const std::string& name, childSetValue("min param text", min_name); childSetValue("max param text", max_name); mLess = getChild("less"); - mLess->setMouseDownCallback( LLScrollingPanelParam::onHintMinMouseDown ); - mLess->setMouseUpCallback( LLScrollingPanelParam::onHintMinMouseUp ); - mLess->setHeldDownCallback( LLScrollingPanelParam::onHintMinHeldDown ); + mLess->setMouseDownCallback( boost::bind(&LLScrollingPanelParam::onHintMinMouseDown, this) ); + mLess->setMouseUpCallback( boost::bind(LLScrollingPanelParam::onHintMinMouseUp, this) ); + mLess->setHeldDownCallback( boost::bind(LLScrollingPanelParam::onHintMinHeldDown, this) ); mLess->setHeldDownDelay( PARAM_STEP_TIME_THRESHOLD ); mMore = getChild("more"); - mMore->setMouseDownCallback( LLScrollingPanelParam::onHintMaxMouseDown ); - mMore->setMouseUpCallback( LLScrollingPanelParam::onHintMaxMouseUp ); - mMore->setHeldDownCallback( LLScrollingPanelParam::onHintMaxHeldDown ); + mMore->setMouseDownCallback( boost::bind(LLScrollingPanelParam::onHintMaxMouseDown, this) ); + mMore->setMouseUpCallback( boost::bind(LLScrollingPanelParam::onHintMaxMouseUp, this) ); + mMore->setHeldDownCallback( boost::bind(LLScrollingPanelParam::onHintMaxHeldDown, this) ); mMore->setHeldDownDelay( PARAM_STEP_TIME_THRESHOLD ); } else @@ -1400,7 +1444,8 @@ LLScrollingPanelParam::LLScrollingPanelParam( const std::string& name, } for (it = to_remove.begin(); it != to_remove.end(); it++) { - removeChild(*it, TRUE); + removeChild(*it); + delete (*it); } slider->translate(0,PARAM_HINT_HEIGHT); reshape(getRect().getWidth(),getRect().getHeight()-PARAM_HINT_HEIGHT); @@ -1526,12 +1571,21 @@ void LLScrollingPanelParam::onSliderMoved(LLUICtrl* ctrl, void* userdata) LLScrollingPanelParam* self = (LLScrollingPanelParam*) userdata; LLViewerVisualParam* param = self->mParam; - F32 current_weight = gAgentAvatarp->getVisualParamWeight( param ); + if(!param) + return; + + LLWearable* wearable = gAgentWearables.getWearable( (LLWearableType::EType)param->getWearableType(), 0 ); // TODO: MULTI-WEARABLE + + if(!wearable) + return; + + F32 current_weight = wearable->getVisualParamWeight(param->getID()); F32 new_weight = self->percentToWeight( (F32)slider->getValue().asReal() ); if (current_weight != new_weight ) { updateAvatarHeightDisplay(); - gAgentAvatarp->setVisualParamWeight( param, new_weight, FALSE); + wearable->setVisualParamWeight( param->getID(), new_weight, FALSE); + wearable->writeToAvatar(); gAgentAvatarp->updateVisualParams(); } } @@ -1593,7 +1647,14 @@ void LLScrollingPanelParam::onHintMaxHeldDown( void* userdata ) void LLScrollingPanelParam::onHintHeldDown( LLVisualParamHint* hint ) { - F32 current_weight = gAgentAvatarp->getVisualParamWeight( hint->getVisualParam() ); + LLViewerVisualParam* param = hint->getVisualParam(); + + LLWearable* wearable = gAgentWearables.getWearable((LLWearableType::EType)param->getWearableType(),0); // TODO: MULTI-WEARABLE + + if(!wearable) + return; + + F32 current_weight = wearable->getVisualParamWeight( param->getID() ); if (current_weight != hint->getVisualParamWeight() ) { @@ -1620,7 +1681,8 @@ void LLScrollingPanelParam::onHintHeldDown( LLVisualParamHint* hint ) if (slider->getMinValue() < new_percent && new_percent < slider->getMaxValue()) { - gAgentAvatarp->setVisualParamWeight( hint->getVisualParam(), new_weight, TRUE); + wearable->setVisualParamWeight(param->getID(), new_weight, TRUE); + wearable->writeToAvatar(); gAgentAvatarp->updateVisualParams(); slider->setValue( weightToPercent( new_weight ) ); @@ -1643,20 +1705,27 @@ void LLScrollingPanelParam::onHintMinMouseUp( void* userdata ) if (elapsed_time < PARAM_STEP_TIME_THRESHOLD) { - // step in direction - F32 current_weight = gAgentAvatarp->getVisualParamWeight( hint->getVisualParam() ); - F32 range = self->mHintMax->getVisualParamWeight() - self->mHintMin->getVisualParamWeight(); - // step a fraction in the negative directiona - F32 new_weight = current_weight - (range / 10.f); - F32 new_percent = self->weightToPercent(new_weight); - LLSliderCtrl* slider = self->getChild("param slider"); - if (slider) + LLViewerVisualParam* param = hint->getVisualParam(); + + LLWearable* wearable = gAgentWearables.getWearable((LLWearableType::EType)param->getWearableType(),0); // TODO: MULTI-WEARABLE + if(wearable) { - if (slider->getMinValue() < new_percent - && new_percent < slider->getMaxValue()) + // step in direction + F32 current_weight = wearable->getVisualParamWeight( param->getID() ); + F32 range = self->mHintMax->getVisualParamWeight() - self->mHintMin->getVisualParamWeight(); + // step a fraction in the negative direction + F32 new_weight = current_weight - (range / 10.f); + F32 new_percent = self->weightToPercent(new_weight); + LLSliderCtrl* slider = self->getChild("param slider"); + if (slider) { - avatar->setVisualParamWeight(hint->getVisualParam(), new_weight, TRUE); - slider->setValue( self->weightToPercent( new_weight ) ); + if (slider->getMinValue() < new_percent + && new_percent < slider->getMaxValue()) + { + wearable->setVisualParamWeight(param->getID(), new_weight, TRUE); + wearable->writeToAvatar(); + slider->setValue( self->weightToPercent( new_weight ) ); + } } } } @@ -1678,20 +1747,27 @@ void LLScrollingPanelParam::onHintMaxMouseUp( void* userdata ) if (elapsed_time < PARAM_STEP_TIME_THRESHOLD) { - // step in direction - F32 current_weight = gAgentAvatarp->getVisualParamWeight( hint->getVisualParam() ); - F32 range = self->mHintMax->getVisualParamWeight() - self->mHintMin->getVisualParamWeight(); - // step a fraction in the negative direction - F32 new_weight = current_weight + (range / 10.f); - F32 new_percent = self->weightToPercent(new_weight); - LLSliderCtrl* slider = self->getChild("param slider"); - if (slider) + LLViewerVisualParam* param = hint->getVisualParam(); + + LLWearable* wearable = gAgentWearables.getWearable((LLWearableType::EType)param->getWearableType(),0); + if(wearable) { - if (slider->getMinValue() < new_percent - && new_percent < slider->getMaxValue()) + // step in direction + F32 current_weight = wearable->getVisualParamWeight( param->getID() ); + F32 range = self->mHintMax->getVisualParamWeight() - self->mHintMin->getVisualParamWeight(); + // step a fraction in the negative direction + F32 new_weight = current_weight + (range / 10.f); + F32 new_percent = self->weightToPercent(new_weight); + LLSliderCtrl* slider = self->getChild("param slider"); + if (slider) { - avatar->setVisualParamWeight(hint->getVisualParam(), new_weight, TRUE); - slider->setValue( self->weightToPercent( new_weight ) ); + if (slider->getMinValue() < new_percent + && new_percent < slider->getMaxValue()) + { + wearable->setVisualParamWeight(param->getID(), new_weight, TRUE); + wearable->writeToAvatar(); + slider->setValue( self->weightToPercent( new_weight ) ); + } } } } @@ -1736,9 +1812,7 @@ struct WearablePanelData LLFloaterCustomize::LLFloaterCustomize() : LLFloater(std::string("customize")), mScrollingPanelList( NULL ), - mInventoryObserver(NULL), - mNextStepAfterSaveCallback( NULL ), - mNextStepAfterSaveUserdata( NULL ) + mInventoryObserver(NULL) { memset(&mWearablePanelList[0],0,sizeof(char*)*LLWearableType::WT_COUNT); //Initialize to 0 @@ -1751,22 +1825,16 @@ LLFloaterCustomize::LLFloaterCustomize() gInventory.addObserver(mInventoryObserver); LLCallbackMap::map_t factory_map; - factory_map["Shape"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SHAPE) ) ); - factory_map["Skin"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SKIN) ) ); - factory_map["Hair"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_HAIR) ) ); - factory_map["Eyes"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_EYES) ) ); - factory_map["Shirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SHIRT) ) ); - factory_map["Pants"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_PANTS) ) ); - factory_map["Shoes"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SHOES) ) ); - factory_map["Socks"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SOCKS) ) ); - factory_map["Jacket"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_JACKET) ) ); - factory_map["Gloves"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_GLOVES) ) ); - factory_map["Undershirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_UNDERSHIRT) ) ); - factory_map["Underpants"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_UNDERPANTS) ) ); - factory_map["Skirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_SKIRT) ) ); - factory_map["Alpha"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_ALPHA))); - factory_map["Tattoo"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_TATTOO))); - factory_map["Physics"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, LLWearableType::WT_PHYSICS))); + const std::string &invalid_name = LLWearableType::getTypeName(LLWearableType::WT_INVALID); + for(U32 type=LLWearableType::WT_SHAPE;typebuildFloater(this, "floater_customize.xml", &factory_map); } @@ -1785,22 +1853,16 @@ BOOL LLFloaterCustomize::postBuild() initWearablePanels(); // Tab container - childSetTabChangeCallback("customize tab container", "Shape", onTabChanged, (void*)LLWearableType::WT_SHAPE, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Skin", onTabChanged, (void*)LLWearableType::WT_SKIN, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Hair", onTabChanged, (void*)LLWearableType::WT_HAIR, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Eyes", onTabChanged, (void*)LLWearableType::WT_EYES, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Shirt", onTabChanged, (void*)LLWearableType::WT_SHIRT, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Pants", onTabChanged, (void*)LLWearableType::WT_PANTS, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Shoes", onTabChanged, (void*)LLWearableType::WT_SHOES, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Socks", onTabChanged, (void*)LLWearableType::WT_SOCKS, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Jacket", onTabChanged, (void*)LLWearableType::WT_JACKET, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Gloves", onTabChanged, (void*)LLWearableType::WT_GLOVES, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Undershirt", onTabChanged, (void*)LLWearableType::WT_UNDERSHIRT, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Underpants", onTabChanged, (void*)LLWearableType::WT_UNDERPANTS, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Skirt", onTabChanged, (void*)LLWearableType::WT_SKIRT, onTabPrecommit ); - childSetTabChangeCallback("customize tab container", "Alpha", onTabChanged, (void*)LLWearableType::WT_ALPHA, onTabPrecommit); - childSetTabChangeCallback("customize tab container", "Tattoo", onTabChanged, (void*)LLWearableType::WT_TATTOO, onTabPrecommit); - childSetTabChangeCallback("customize tab container", "Physics", onTabChanged, (void*)LLWearableType::WT_PHYSICS, onTabPrecommit); + const std::string &invalid_name = LLWearableType::getTypeName(LLWearableType::WT_INVALID); + for(U32 type=LLWearableType::WT_SHAPE;typeinvalidateAll(); + gAgentAvatarp->invalidateAll(); - avatar->requestLayerSetUploads(); + gAgentAvatarp->requestLayerSetUploads(); gAgent.sendAgentSetAppearance(); } @@ -2042,7 +2103,8 @@ void LLFloaterCustomize::onMakeOutfitCommit( LLMakeOutfitDialog* dialog, void* u dialog->getIncludedItems( wearables_to_include, attachments_to_include ); - gAgentWearables.makeNewOutfit( dialog->getFolderName(), wearables_to_include, attachments_to_include, dialog->getRenameClothing() ); + // MULTI-WEARABLES TODO + //LLAppearanceMgr::getInstance()->makeNewOutfit( dialog->getFolderName(), wearables_to_include, attachments_to_include, dialog->getRenameClothing() ); } } @@ -2069,79 +2131,42 @@ void* LLFloaterCustomize::createWearablePanel(void* userdata) void LLFloaterCustomize::initWearablePanels() { - LLSubpart* part; - ///////////////////////////////////////// // Shape LLPanelEditWearable* panel = mWearablePanelList[ LLWearableType::WT_SHAPE ]; // body - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "shape_body"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); + LLSubpart* part = new LLSubpart("mPelvis", "shape_body", LLVector3d(0.f,0.f,0.1f), LLVector3d(-2.5f, 0.5f, 0.8f)); panel->addSubpart( "Body", SUBPART_SHAPE_WHOLE, part ); // head supparts - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_head"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + const LLVector3d head_target(0.f, 0.f, 0.05f); + const LLVector3d head_camera(-0.5f, 0.05f, 0.07f); + + part = new LLSubpart("mHead", "shape_head", head_target, head_camera); panel->addSubpart( "Head", SUBPART_SHAPE_HEAD, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_eyes"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + part = new LLSubpart("mHead", "shape_eyes", head_target, head_camera); panel->addSubpart( "Eyes", SUBPART_SHAPE_EYES, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_ears"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + part = new LLSubpart("mHead", "shape_ears", head_target, head_camera); panel->addSubpart( "Ears", SUBPART_SHAPE_EARS, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_nose"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + part = new LLSubpart("mHead", "shape_nose", head_target, head_camera); panel->addSubpart( "Nose", SUBPART_SHAPE_NOSE, part ); - - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_mouth"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + part = new LLSubpart("mHead", "shape_mouth", head_target, head_camera); panel->addSubpart( "Mouth", SUBPART_SHAPE_MOUTH, part ); - - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "shape_chin"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f ); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f ); + part = new LLSubpart("mHead", "shape_chin", head_target, head_camera); panel->addSubpart( "Chin", SUBPART_SHAPE_CHIN, part ); // torso - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "shape_torso"; - part->mTargetOffset.setVec(0.f, 0.f, 0.3f); - part->mCameraOffset.setVec(-1.f, 0.15f, 0.3f); + part = new LLSubpart("mTorso", "shape_torso", LLVector3d(0.f, 0.f, 0.3f), LLVector3d(-1.f, 0.15f, 0.3f)); panel->addSubpart( "Torso", SUBPART_SHAPE_TORSO, part ); // legs - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "shape_legs"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "shape_legs", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( "Legs", SUBPART_SHAPE_LEGS, part ); panel->childSetCommitCallback("sex radio", LLPanelEditWearable::onCommitSexChange, panel); @@ -2150,32 +2175,16 @@ void LLFloaterCustomize::initWearablePanels() // Skin panel = mWearablePanelList[ LLWearableType::WT_SKIN ]; - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "skin_color"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "skin_color", head_target, head_camera); panel->addSubpart( "Skin Color", SUBPART_SKIN_COLOR, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "skin_facedetail"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "skin_facedetail", head_target, head_camera); panel->addSubpart( "Face Detail", SUBPART_SKIN_FACEDETAIL, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "skin_makeup"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "skin_makeup", head_target, head_camera); panel->addSubpart( "Makeup", SUBPART_SKIN_MAKEUP, part ); - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "skin_bodydetail"; - part->mTargetOffset.setVec(0.f, 0.f, -0.2f); - part->mCameraOffset.setVec(-2.5f, 0.5f, 0.5f); + part = new LLSubpart("mPelvis", "skin_bodydetail", LLVector3d(0.f, 0.f, -0.2f), LLVector3d(-2.5f, 0.5f, 0.5f)); panel->addSubpart( "Body Detail", SUBPART_SKIN_BODYDETAIL, part ); panel->addTextureDropTarget( TEX_HEAD_BODYPAINT, "Head Tattoos", LLUUID::null, TRUE ); @@ -2186,33 +2195,16 @@ void LLFloaterCustomize::initWearablePanels() // Hair panel = mWearablePanelList[ LLWearableType::WT_HAIR ]; - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "hair_color"; - part->mTargetOffset.setVec(0.f, 0.f, 0.10f); - part->mCameraOffset.setVec(-0.4f, 0.05f, 0.10f); + part = new LLSubpart("mHead", "hair_color", LLVector3d(0.f, 0.f, 0.10f), LLVector3d(-0.4f, 0.05f, 0.10f)); panel->addSubpart( "Color", SUBPART_HAIR_COLOR, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "hair_style"; - part->mTargetOffset.setVec(0.f, 0.f, 0.10f); - part->mCameraOffset.setVec(-0.4f, 0.05f, 0.10f); + part = new LLSubpart("mHead", "hair_style", head_target, head_camera); panel->addSubpart( "Style", SUBPART_HAIR_STYLE, part ); - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "hair_eyebrows"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "hair_eyebrows", head_target, head_camera); panel->addSubpart( "Eyebrows", SUBPART_HAIR_EYEBROWS, part ); - part = new LLSubpart(); - part->mSex = SEX_MALE; - part->mTargetJoint = "mHead"; - part->mEditGroup = "hair_facial"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "hair_facial", head_target, head_camera, SEX_MALE); panel->addSubpart( "Facial", SUBPART_HAIR_FACIAL, part ); panel->addTextureDropTarget(TEX_HAIR, "Texture", @@ -2223,11 +2215,7 @@ void LLFloaterCustomize::initWearablePanels() // Eyes panel = mWearablePanelList[ LLWearableType::WT_EYES ]; - part = new LLSubpart(); - part->mTargetJoint = "mHead"; - part->mEditGroup = "eyes"; - part->mTargetOffset.setVec(0.f, 0.f, 0.05f); - part->mCameraOffset.setVec(-0.5f, 0.05f, 0.07f); + part = new LLSubpart("mHead", "eyes", head_target, head_camera); panel->addSubpart( LLStringUtil::null, SUBPART_EYES, part ); panel->addTextureDropTarget(TEX_EYES_IRIS, "Iris", @@ -2238,11 +2226,7 @@ void LLFloaterCustomize::initWearablePanels() // Shirt panel = mWearablePanelList[ LLWearableType::WT_SHIRT ]; - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "shirt"; - part->mTargetOffset.setVec(0.f, 0.f, 0.3f); - part->mCameraOffset.setVec(-1.f, 0.15f, 0.3f); + part = new LLSubpart("mTorso", "shirt", LLVector3d(0.f, 0.f, 0.3f), LLVector3d(-1.f, 0.15f, 0.3f)); panel->addSubpart( LLStringUtil::null, SUBPART_SHIRT, part ); panel->addTextureDropTarget( TEX_UPPER_SHIRT, "Fabric", @@ -2256,11 +2240,7 @@ void LLFloaterCustomize::initWearablePanels() // Pants panel = mWearablePanelList[ LLWearableType::WT_PANTS ]; - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "pants"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "pants", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( LLStringUtil::null, SUBPART_PANTS, part ); panel->addTextureDropTarget(TEX_LOWER_PANTS, "Fabric", @@ -2276,11 +2256,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "shoes"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "shoes", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( LLStringUtil::null, SUBPART_SHOES, part ); panel->addTextureDropTarget( TEX_LOWER_SHOES, "Fabric", @@ -2297,11 +2273,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "socks"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "socks", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( LLStringUtil::null, SUBPART_SOCKS, part ); panel->addTextureDropTarget( TEX_LOWER_SOCKS, "Fabric", @@ -2317,11 +2289,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "jacket"; - part->mTargetOffset.setVec(0.f, 0.f, 0.f); - part->mCameraOffset.setVec(-2.f, 0.1f, 0.3f); + part = new LLSubpart("mTorso", "jacket", LLVector3d(), LLVector3d(-2.f, 0.1f, 0.3f)); panel->addSubpart( LLStringUtil::null, SUBPART_JACKET, part ); panel->addTextureDropTarget( TEX_UPPER_JACKET, "Upper Fabric", @@ -2340,11 +2308,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "skirt"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "skirt", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( LLStringUtil::null, SUBPART_SKIRT, part ); panel->addTextureDropTarget( TEX_SKIRT, "Fabric", @@ -2361,11 +2325,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "gloves"; - part->mTargetOffset.setVec(0.f, 0.f, 0.f); - part->mCameraOffset.setVec(-1.f, 0.15f, 0.f); + part = new LLSubpart("mTorso", "gloves", LLVector3d(), LLVector3d(-1.f, 0.15f, 0.f)); panel->addSubpart( LLStringUtil::null, SUBPART_GLOVES, part ); panel->addTextureDropTarget( TEX_UPPER_GLOVES, "Fabric", @@ -2382,11 +2342,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "undershirt"; - part->mTargetOffset.setVec(0.f, 0.f, 0.3f); - part->mCameraOffset.setVec(-1.f, 0.15f, 0.3f); + part = new LLSubpart("mTorso", "undershirt", LLVector3d(0.f, 0.f, 0.3f), LLVector3d(-1.f, 0.15f, 0.3f)); panel->addSubpart( LLStringUtil::null, SUBPART_UNDERSHIRT, part ); panel->addTextureDropTarget( TEX_UPPER_UNDERSHIRT, "Fabric", @@ -2402,11 +2358,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "underpants"; - part->mTargetOffset.setVec(0.f, 0.f, -0.5f); - part->mCameraOffset.setVec(-1.6f, 0.15f, -0.5f); + part = new LLSubpart("mPelvis", "underpants", LLVector3d(0.f, 0.f, -0.5f), LLVector3d(-1.6f, 0.15f, -0.5f)); panel->addSubpart( LLStringUtil::null, SUBPART_UNDERPANTS, part ); panel->addTextureDropTarget( TEX_LOWER_UNDERPANTS, "Fabric", @@ -2422,11 +2374,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "alpha"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); + part = new LLSubpart("mPelvis", "alpha", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-2.5f, 0.5f, 0.8f)); panel->addSubpart(LLStringUtil::null, SUBPART_ALPHA, part); panel->addTextureDropTarget(TEX_LOWER_ALPHA, "Lower Alpha", @@ -2458,11 +2406,7 @@ void LLFloaterCustomize::initWearablePanels() if (panel) { - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "tattoo"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); + part = new LLSubpart("mPelvis", "tattoo", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-2.5f, 0.5f, 0.8f)); panel->addSubpart(LLStringUtil::null, SUBPART_TATTOO, part); panel->addTextureDropTarget(TEX_LOWER_TATTOO, "Lower Tattoo", @@ -2484,63 +2428,25 @@ void LLFloaterCustomize::initWearablePanels() if(panel) { - part = new LLSubpart(); - part->mSex = SEX_FEMALE; - part->mTargetJoint = "mTorso"; - part->mEditGroup = "physics_breasts_updown"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-0.8f, 0.15f, 0.38f); - part->mVisualHint = false; + part = new LLSubpart("mTorso", "physics_breasts_updown", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-0.8f, 0.15f, 0.38f), SEX_FEMALE, false); panel->addSubpart("Breast Bounce", SUBPART_PHYSICS_BREASTS_UPDOWN, part); - part = new LLSubpart(); - part->mSex = SEX_FEMALE; - part->mTargetJoint = "mTorso"; - part->mEditGroup = "physics_breasts_inout"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-0.8f, 0.15f, 0.38f); - part->mVisualHint = false; + part = new LLSubpart("mTorso", "physics_breasts_inout", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-0.8f, 0.15f, 0.38f), SEX_FEMALE, false); panel->addSubpart("Breast Cleavage", SUBPART_PHYSICS_BREASTS_INOUT, part); - part = new LLSubpart(); - part->mSex = SEX_FEMALE; - part->mTargetJoint = "mTorso"; - part->mEditGroup = "physics_breasts_leftright"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-0.8f, 0.15f, 0.38f); - part->mVisualHint = false; + part = new LLSubpart("mTorso", "physics_breasts_leftright", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-0.8f, 0.15f, 0.38f), SEX_FEMALE, false); panel->addSubpart("Breast Sway", SUBPART_PHYSICS_BREASTS_LEFTRIGHT, part); - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "physics_belly_updown"; - part->mTargetOffset.setVec(0.f, 0.f, -.05f); - part->mCameraOffset.setVec(-0.8f, 0.15f, 0.38f); - part->mVisualHint = false; + part = new LLSubpart("mTorso", "physics_belly_updown", LLVector3d(0.f, 0.f, -.05f), LLVector3d(-0.8f, 0.15f, 0.38f), false); panel->addSubpart("Belly Bounce", SUBPART_PHYSICS_BELLY_UPDOWN, part); - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "physics_butt_updown"; - part->mTargetOffset.setVec(0.f, 0.f, -0.1f); - part->mCameraOffset.setVec(0.3f, 0.8f, -0.1f); - part->mVisualHint = false; + part = new LLSubpart("mPelvis", "physics_butt_updown", LLVector3d(0.f, 0.f,-.1f), LLVector3d(0.3f, 0.8f, -0.1f), false); panel->addSubpart("Butt Bounce", SUBPART_PHYSICS_BUTT_UPDOWN, part); - part = new LLSubpart(); - part->mTargetJoint = "mPelvis"; - part->mEditGroup = "physics_butt_leftright"; - part->mTargetOffset.setVec(0.f, 0.f, -0.1f); - part->mCameraOffset.setVec(0.3f, 0.8f, -0.1f); - part->mVisualHint = false; + part = new LLSubpart("mPelvis", "physics_butt_leftright", LLVector3d(0.f, 0.f,-.1f), LLVector3d(0.3f, 0.8f, -0.1f), false); panel->addSubpart("Butt Sway", SUBPART_PHYSICS_BUTT_LEFTRIGHT, part); - part = new LLSubpart(); - part->mTargetJoint = "mTorso"; - part->mEditGroup = "physics_advanced"; - part->mTargetOffset.setVec(0.f, 0.f, 0.1f); - part->mCameraOffset.setVec(-2.5f, 0.5f, 0.8f); - part->mVisualHint = false; + part = new LLSubpart("mTorso", "physics_advanced", LLVector3d(0.f, 0.f, 0.1f), LLVector3d(-2.5f, 0.5f, 0.8f), false); panel->addSubpart("Advanced Parameters", SUBPART_PHYSICS_ADVANCED, part); } @@ -2602,11 +2508,11 @@ void LLFloaterCustomize::onTabPrecommit( void* userdata, bool from_click ) LLWearableType::EType type = (LLWearableType::EType)(intptr_t) userdata; if (type != LLWearableType::WT_INVALID && gFloaterCustomize && gFloaterCustomize->getCurrentWearableType() != type) { - gFloaterCustomize->askToSaveIfDirty(onCommitChangeTab, userdata); + gFloaterCustomize->askToSaveIfDirty(boost::bind(&onCommitChangeTab, _1)); } else { - onCommitChangeTab(TRUE, NULL); + onCommitChangeTab(true); } } @@ -2629,7 +2535,7 @@ void LLFloaterCustomize::onClose(bool app_quitting) } // static -void LLFloaterCustomize::onCommitChangeTab(BOOL proceed, void* userdata) +void LLFloaterCustomize::onCommitChangeTab(BOOL proceed) { if (!proceed || !gFloaterCustomize) { @@ -2716,24 +2622,20 @@ void LLFloaterCustomize::updateScrollingPanelList(BOOL allow_modify) } -void LLFloaterCustomize::askToSaveIfDirty( void(*next_step_callback)(BOOL proceed, void* userdata), void* userdata ) +void LLFloaterCustomize::askToSaveIfDirty( boost::function cb ) { if( isDirty()) { // Ask if user wants to save, then continue to next step afterwards - mNextStepAfterSaveCallback = next_step_callback; - mNextStepAfterSaveUserdata = userdata; + mNextStepAfterSaveCallback.connect(cb); // Bring up view-modal dialog: Save changes? Yes, No, Cancel LLNotificationsUtil::add("SaveClothingBodyChanges", LLSD(), LLSD(), boost::bind(&LLFloaterCustomize::onSaveDialog, this, _1, _2)); - return; } - - // Try to move to the next step - if( next_step_callback ) + else { - next_step_callback( TRUE, userdata ); + cb(TRUE); //just clal it immediately. } } @@ -2767,10 +2669,9 @@ bool LLFloaterCustomize::onSaveDialog(const LLSD& notification, const LLSD& resp break; } - if( mNextStepAfterSaveCallback ) - { - mNextStepAfterSaveCallback( proceed, mNextStepAfterSaveUserdata ); - } + mNextStepAfterSaveCallback(proceed); + mNextStepAfterSaveCallback.disconnect_all_slots(); //Should this be done? + return false; } diff --git a/indra/newview/llfloatercustomize.h b/indra/newview/llfloatercustomize.h index 4bf4ef708..c19a570ad 100644 --- a/indra/newview/llfloatercustomize.h +++ b/indra/newview/llfloatercustomize.h @@ -99,7 +99,7 @@ public: virtual BOOL isDirty() const; - void askToSaveIfDirty( void(*next_step_callback)(BOOL proceed, void* userdata), void* userdata ); + void askToSaveIfDirty( boost::function cb ); void switchToDefaultSubpart(); @@ -118,7 +118,7 @@ public: static void onTabChanged( void* userdata, bool from_click ); static void onTabPrecommit( void* userdata, bool from_click ); bool onSaveDialog(const LLSD& notification, const LLSD& response); - static void onCommitChangeTab(BOOL proceed, void* userdata); + static void onCommitChangeTab(BOOL proceed); void fetchInventory(); void updateInventoryUI(); @@ -135,10 +135,8 @@ protected: LLInventoryObserver* mInventoryObserver; - void (*mNextStepAfterSaveCallback)(BOOL proceed, void* userdata); - void* mNextStepAfterSaveUserdata; - - + boost::signals2::signal mNextStepAfterSaveCallback; + protected: static void* createWearablePanel(void* userdata); diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 67c20c91d..9ac2f29ba 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -264,7 +264,7 @@ void LLFloaterImagePreview::draw() if (selected <= 0) { - gl_rect_2d_checkerboard( getScreenRect(), mPreviewRect); + gl_rect_2d_checkerboard( calcScreenRect(), mPreviewRect); LLGLDisable gls_alpha(GL_ALPHA_TEST); if(mImagep.notNull()) diff --git a/indra/newview/llfloaterlagmeter.cpp b/indra/newview/llfloaterlagmeter.cpp index a2d120ad8..d1445cdbb 100644 --- a/indra/newview/llfloaterlagmeter.cpp +++ b/indra/newview/llfloaterlagmeter.cpp @@ -147,25 +147,25 @@ void LLFloaterLagMeter::determineClient() if (!gFocusMgr.getAppHasFocus()) { - mClientButton->setImageUnselected(LAG_GOOD_IMAGE_NAME); + mClientButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); mClientText->setText( getString("client_frame_time_window_bg_msg", mStringArgs) ); mClientCause->setText( LLStringUtil::null ); } else if(client_frame_time >= mClientFrameTimeCritical) { - mClientButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME); + mClientButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); mClientText->setText( getString("client_frame_time_critical_msg", mStringArgs) ); find_cause = true; } else if(client_frame_time >= mClientFrameTimeWarning) { - mClientButton->setImageUnselected(LAG_WARNING_IMAGE_NAME); + mClientButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); mClientText->setText( getString("client_frame_time_warning_msg", mStringArgs) ); find_cause = true; } else { - mClientButton->setImageUnselected(LAG_GOOD_IMAGE_NAME); + mClientButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); mClientText->setText( getString("client_frame_time_normal_msg", mStringArgs) ); mClientCause->setText( LLStringUtil::null ); } @@ -206,13 +206,13 @@ void LLFloaterLagMeter::determineNetwork() if(packet_loss >= mNetworkPacketLossCritical) { - mNetworkButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME); + mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); mNetworkText->setText( getString("network_packet_loss_critical_msg", mStringArgs) ); find_cause_loss = true; } else if(ping_time >= mNetworkPingCritical) { - mNetworkButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME); + mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); if (client_frame_time_ms < mNetworkPingCritical) { mNetworkText->setText( getString("network_ping_critical_msg", mStringArgs) ); @@ -221,13 +221,13 @@ void LLFloaterLagMeter::determineNetwork() } else if(packet_loss >= mNetworkPacketLossWarning) { - mNetworkButton->setImageUnselected(LAG_WARNING_IMAGE_NAME); + mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); mNetworkText->setText( getString("network_packet_loss_warning_msg", mStringArgs) ); find_cause_loss = true; } else if(ping_time >= mNetworkPingWarning) { - mNetworkButton->setImageUnselected(LAG_WARNING_IMAGE_NAME); + mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); if (client_frame_time_ms < mNetworkPingWarning) { mNetworkText->setText( getString("network_ping_warning_msg", mStringArgs) ); @@ -236,7 +236,7 @@ void LLFloaterLagMeter::determineNetwork() } else { - mNetworkButton->setImageUnselected(LAG_GOOD_IMAGE_NAME); + mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); mNetworkText->setText( getString("network_performance_normal_msg", mStringArgs) ); } @@ -261,19 +261,19 @@ void LLFloaterLagMeter::determineServer() if(sim_frame_time >= mServerFrameTimeCritical) { - mServerButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME); + mServerButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); mServerText->setText( getString("server_frame_time_critical_msg", mStringArgs) ); find_cause = true; } else if(sim_frame_time >= mServerFrameTimeWarning) { - mServerButton->setImageUnselected(LAG_WARNING_IMAGE_NAME); + mServerButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); mServerText->setText( getString("server_frame_time_warning_msg", mStringArgs) ); find_cause = true; } else { - mServerButton->setImageUnselected(LAG_GOOD_IMAGE_NAME); + mServerButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); mServerText->setText( getString("server_frame_time_normal_msg", mStringArgs) ); mServerCause->setText( LLStringUtil::null ); } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 14260b1dc..9f9e2d308 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1105,7 +1105,7 @@ BOOL LLPanelLandObjects::postBuild() mSelectedObjects = getChild("selected_objects_text"); mCleanOtherObjectsTime = getChild("clean other time"); - mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus, this); + mCleanOtherObjectsTime->setFocusLostCallback(boost::bind(&LLPanelLandObjects::onLostFocus, _1, this)); mCleanOtherObjectsTime->setCommitCallback(onCommitClean); childSetPrevalidate("clean other time", LLLineEditor::prevalidateNonNegativeS32); diff --git a/indra/newview/llfloaterlandmark.cpp b/indra/newview/llfloaterlandmark.cpp index 42bbbe0d7..2f39ac963 100644 --- a/indra/newview/llfloaterlandmark.cpp +++ b/indra/newview/llfloaterlandmark.cpp @@ -93,7 +93,7 @@ LLFloaterLandmark::LLFloaterLandmark(const LLSD& data) mInventoryPanel->setFilterTypes(filter_types); //mInventoryPanel->setFilterPermMask(getFilterPermMask()); //Commented out due to no-copy texture loss. - mInventoryPanel->setSelectCallback(onSelectionChange, this); + mInventoryPanel->setSelectCallback(boost::bind(&LLFloaterLandmark::onSelectionChange, _1, _2, (void*)this)); mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); mInventoryPanel->setAllowMultiSelect(FALSE); diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index 24f976e3c..ae60a6a9b 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -50,7 +50,7 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" #include "llinventorypanel.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llselectmgr.h" #include "lluiconstants.h" #include "llviewerobject.h" @@ -157,54 +157,38 @@ void LLFloaterOpenObject::moveToInventory(bool wear) { parent_category_id = gInventory.getRootFolderID(); } - + LLCategoryCreate* cat_data = new LLCategoryCreate(object_id, wear); - + LLUUID category_id = gInventory.createNewCategory(parent_category_id, - LLFolderType::FT_NONE, - name, - callbackCreateInventoryCategory, - (void*)cat_data); + LLFolderType::FT_NONE, + name, + callbackCreateInventoryCategory, + (void*)cat_data); //If we get a null category ID, we are using a capability in createNewCategory and we will //handle the following in the callbackCreateInventoryCategory routine. if ( category_id.notNull() ) { - delete cat_data; - - LLCatAndWear* data = new LLCatAndWear; - data->mCatID = category_id; - data->mWear = wear; - data->mFolderResponded = false; - - // Copy and/or move the items into the newly created folder. - // Ignore any "you're going to break this item" messages. - BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE, - callbackMoveInventory, - (void*)data); - if (!success) - { - delete data; - data = NULL; - - LLNotificationsUtil::add("OpenObjectCannotCopy"); - } + LLSD result; + result["folder_id"] = category_id; + //Reduce redundant code by just calling the callback. Dur. + callbackCreateInventoryCategory(result,cat_data); } } - // static void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLSD& result, void* data) { LLCategoryCreate* cat_data = (LLCategoryCreate*)data; - + LLUUID category_id = result["folder_id"].asUUID(); LLCatAndWear* wear_data = new LLCatAndWear; wear_data->mCatID = category_id; wear_data->mWear = cat_data->mWear; wear_data->mFolderResponded = true; - + // Copy and/or move the items into the newly created folder. // Ignore any "you're going to break this item" messages. BOOL success = move_inv_category_world_to_agent(cat_data->mObjectID, category_id, TRUE, @@ -227,11 +211,10 @@ void LLFloaterOpenObject::callbackMoveInventory(S32 result, void* data) if (result == 0) { - LLInventoryView::showAgentInventory(); - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if (view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(cat->mCatID, TAKE_FOCUS_NO); + active_panel->setSelection(cat->mCatID, TAKE_FOCUS_NO); } } @@ -259,6 +242,6 @@ void LLFloaterOpenObject::onClickMoveAndWear(void* data) void* LLFloaterOpenObject::createPanelInventory(void* data) { LLFloaterOpenObject* floater = (LLFloaterOpenObject*)data; - floater->mPanelInventory = new LLPanelInventory(std::string("Object Contents"), LLRect()); + floater->mPanelInventory = new LLPanelObjectInventory(std::string("Object Contents"), LLRect()); return floater->mPanelInventory; } diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h index 8c00cc7f7..7639fd9f8 100644 --- a/indra/newview/llfloateropenobject.h +++ b/indra/newview/llfloateropenobject.h @@ -41,7 +41,7 @@ #include "llfloater.h" class LLObjectSelection; -class LLPanelInventory; +class LLPanelObjectInventory; class LLFloaterOpenObject : public LLFloater @@ -84,7 +84,7 @@ protected: protected: static LLFloaterOpenObject* sInstance; - LLPanelInventory* mPanelInventory; + LLPanelObjectInventory* mPanelInventory; LLSafeHandle mObjectSelection; BOOL mDirty; }; diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp index f4aa77aa9..6ff9d6695 100644 --- a/indra/newview/llfloaterpostcard.cpp +++ b/indra/newview/llfloaterpostcard.cpp @@ -135,7 +135,7 @@ BOOL LLFloaterPostcard::postBuild() MsgField->setWordWrap(TRUE); // For the first time a user focusess to .the msg box, all text will be selected. - MsgField->setFocusChangedCallback(onMsgFormFocusRecieved, this); + MsgField->setFocusChangedCallback(boost::bind(&LLFloaterPostcard::onMsgFormFocusRecieved, this, _1, MsgField)); } childSetFocus("to_form", TRUE); @@ -339,17 +339,12 @@ void LLFloaterPostcard::updateUserInfo(const std::string& email) } } -void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data) +void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, LLTextEditor* msg_form) { - LLFloaterPostcard* self = (LLFloaterPostcard *)data; - if(self) + if(msg_form && msg_form == receiver && msg_form->hasFocus() && !(mHasFirstMsgFocus)) { - LLTextEditor* msgForm = self->getChild("msg_form"); - if(msgForm && msgForm == receiver && msgForm->hasFocus() && !(self->mHasFirstMsgFocus)) - { - self->mHasFirstMsgFocus = true; - msgForm->setText(LLStringUtil::null); - } + mHasFirstMsgFocus = true; + msg_form->setText(LLStringUtil::null); } } diff --git a/indra/newview/llfloaterpostcard.h b/indra/newview/llfloaterpostcard.h index bce14609d..e2ad154ef 100644 --- a/indra/newview/llfloaterpostcard.h +++ b/indra/newview/llfloaterpostcard.h @@ -68,7 +68,7 @@ public: static void updateUserInfo(const std::string& email); - static void onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data); + void onMsgFormFocusRecieved(LLFocusableElement* receiver, LLTextEditor* msg_form); bool missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response); void sendPostcard(); diff --git a/indra/newview/llfloaterstats.cpp b/indra/newview/llfloaterstats.cpp index fa71cfb13..fb9b1cc24 100644 --- a/indra/newview/llfloaterstats.cpp +++ b/indra/newview/llfloaterstats.cpp @@ -613,7 +613,7 @@ void LLFloaterStats::reshape(S32 width, S32 height, BOOL called_from_parent) void LLFloaterStats::addStatView(LLStatView* stat) { - mStatsContainer->addChildAtEnd(stat); + mStatsContainer->addChildInBack(stat); } // virtual diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 385a45306..913051f61 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -51,7 +51,7 @@ #include "llpanelcontents.h" #include "llpanelface.h" #include "llpanelland.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llpanelobject.h" #include "llpanelvolume.h" #include "llpanelpermissions.h" @@ -168,7 +168,7 @@ void* LLFloaterTools::createPanelContents(void* data) void* LLFloaterTools::createPanelContentsInventory(void* data) { LLFloaterTools* floater = (LLFloaterTools*)data; - floater->mPanelContents->mPanelInventory = new LLPanelInventory(std::string("ContentsInventory"), LLRect()); + floater->mPanelContents->mPanelInventory = new LLPanelObjectInventory(std::string("ContentsInventory"), LLRect()); return floater->mPanelContents->mPanelInventory; } @@ -180,8 +180,41 @@ void* LLFloaterTools::createPanelLandInfo(void* data) return floater->mPanelLandInfo; } +static const std::string toolNames[]={ + "ToolCube", + "ToolPrism", + "ToolPyramid", + "ToolTetrahedron", + "ToolCylinder", + "ToolHemiCylinder", + "ToolCone", + "ToolHemiCone", + "ToolSphere", + "ToolHemiSphere", + "ToolTorus", + "ToolTube", + "ToolRing", + "ToolTree", + "ToolGrass"}; +LLPCode toolData[]={ + LL_PCODE_CUBE, + LL_PCODE_PRISM, + LL_PCODE_PYRAMID, + LL_PCODE_TETRAHEDRON, + LL_PCODE_CYLINDER, + LL_PCODE_CYLINDER_HEMI, + LL_PCODE_CONE, + LL_PCODE_CONE_HEMI, + LL_PCODE_SPHERE, + LL_PCODE_SPHERE_HEMI, + LL_PCODE_TORUS, + LLViewerObject::LL_VO_SQUARE_TORUS, + LLViewerObject::LL_VO_TRIANGLE_TORUS, + LL_PCODE_LEGACY_TREE, + LL_PCODE_LEGACY_GRASS}; + BOOL LLFloaterTools::postBuild() -{ +{ // Hide until tool selected setVisible(FALSE); @@ -250,44 +283,12 @@ BOOL LLFloaterTools::postBuild() // Create Buttons // - static const std::string toolNames[]={ - "ToolCube", - "ToolPrism", - "ToolPyramid", - "ToolTetrahedron", - "ToolCylinder", - "ToolHemiCylinder", - "ToolCone", - "ToolHemiCone", - "ToolSphere", - "ToolHemiSphere", - "ToolTorus", - "ToolTube", - "ToolRing", - "ToolTree", - "ToolGrass"}; - void* toolData[]={ - &LLToolPlacerPanel::sCube, - &LLToolPlacerPanel::sPrism, - &LLToolPlacerPanel::sPyramid, - &LLToolPlacerPanel::sTetrahedron, - &LLToolPlacerPanel::sCylinder, - &LLToolPlacerPanel::sCylinderHemi, - &LLToolPlacerPanel::sCone, - &LLToolPlacerPanel::sConeHemi, - &LLToolPlacerPanel::sSphere, - &LLToolPlacerPanel::sSphereHemi, - &LLToolPlacerPanel::sTorus, - &LLToolPlacerPanel::sSquareTorus, - &LLToolPlacerPanel::sTriangleTorus, - &LLToolPlacerPanel::sTree, - &LLToolPlacerPanel::sGrass}; for(size_t t=0; t(toolNames[t]); if(found) { - found->setClickedCallback(setObjectType,toolData[t]); + found->setClickedCallback(boost::bind(&LLFloaterTools::setObjectType, toolData[t])); mButtons.push_back( found ); } else @@ -737,15 +738,13 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) else { // Highlight the correct placer button - for( std::vector::size_type i = 0; i < mButtons.size(); i++ ) + for( S32 t = 0; t < (S32)mButtons.size(); t++ ) { LLPCode pcode = LLToolPlacer::getObjectType(); - void *userdata = mButtons[i]->getCallbackUserData(); - LLPCode *cur = (LLPCode*) userdata; - - BOOL state = (pcode == *cur); - mButtons[i]->setToggleState( state ); - mButtons[i]->setVisible( create_visible ); + LLPCode button_pcode = toolData[t]; + BOOL state = (pcode == button_pcode); + mButtons[t]->setToggleState( state ); + mButtons[t]->setVisible( create_visible ); } } @@ -1034,9 +1033,8 @@ void commit_grid_mode(LLUICtrl *ctrl, void *data) } // static -void LLFloaterTools::setObjectType( void* data ) +void LLFloaterTools::setObjectType( LLPCode pcode ) { - LLPCode pcode = *(LLPCode*) data; LLToolPlacer::setObjectType( pcode ); gSavedSettings.setBOOL("CreateToolCopySelection", FALSE); gFocusMgr.setMouseCapture(NULL); @@ -1067,11 +1065,11 @@ void LLFloaterTools::onSelectTreesGrass(LLUICtrl*, void*) { const std::string &selected = gFloaterTools->mComboTreesGrass->getValue(); LLPCode pcode = LLToolPlacer::getObjectType(); - if (pcode == LLToolPlacerPanel::sTree) + if (pcode == LL_PCODE_LEGACY_TREE) { gSavedSettings.setString("LastTree", selected); } - else if (pcode == LLToolPlacerPanel::sGrass) + else if (pcode == LL_PCODE_LEGACY_GRASS) { gSavedSettings.setString("LastGrass", selected); } @@ -1085,7 +1083,7 @@ void LLFloaterTools::updateTreeGrassCombo(bool visible) LLPCode pcode = LLToolPlacer::getObjectType(); std::map::iterator it, end; std::string selected; - if (pcode == LLToolPlacerPanel::sTree) + if (pcode == LL_PCODE_LEGACY_TREE) { tree_grass_label->setVisible(visible); LLButton* button = getChild("ToolTree"); @@ -1095,7 +1093,7 @@ void LLFloaterTools::updateTreeGrassCombo(bool visible) it = LLVOTree::sSpeciesNames.begin(); end = LLVOTree::sSpeciesNames.end(); } - else if (pcode == LLToolPlacerPanel::sGrass) + else if (pcode == LL_PCODE_LEGACY_GRASS) { tree_grass_label->setVisible(visible); LLButton* button = getChild("ToolGrass"); diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h index a06e2728c..4a8078a44 100644 --- a/indra/newview/llfloatertools.h +++ b/indra/newview/llfloatertools.h @@ -103,10 +103,10 @@ public: static void setEditTool(void* data); void saveLastTool(); private: - static void setObjectType( void* data ); - + void refresh(); + static void setObjectType( LLPCode pcode ); static void onClickGridOptions(void* data); public: diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 5e6ecf2b4..8cd9ea972 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -208,13 +208,9 @@ BOOL LLFloaterWorldMap::postBuild() childSetAction("DoSearch", onLocationCommit, this); - childSetFocusChangedCallback("location", onLocationFocusChanged, this); - LLLineEditor *location_editor = getChild("location"); - if (location_editor) - { - location_editor->setKeystrokeCallback( onSearchTextEntry ); - } + location_editor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1)); + location_editor->setKeystrokeCallback( onSearchTextEntry ); childSetCommitCallback("search_results", onCommitSearchResult, this); childSetDoubleClickCallback("search_results", onClickTeleportBtn); @@ -1132,7 +1128,7 @@ void LLFloaterWorldMap::onComboTextEntry( LLLineEditor* ctrl, void* userdata ) void LLFloaterWorldMap::onSearchTextEntry( LLLineEditor* ctrl, void* userdata ) { onComboTextEntry(ctrl, userdata); - updateSearchEnabled(ctrl, userdata); + gFloaterWorldMap->updateSearchEnabled(); } // static @@ -1239,24 +1235,21 @@ void LLFloaterWorldMap::onAvatarComboCommit( LLUICtrl* ctrl, void* userdata ) } } -//static -void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus, void* userdata ) +void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus ) { - updateSearchEnabled((LLUICtrl*)focus, userdata); + updateSearchEnabled(); } -// static -void LLFloaterWorldMap::updateSearchEnabled( LLUICtrl* ctrl, void* userdata ) +void LLFloaterWorldMap::updateSearchEnabled() { - LLFloaterWorldMap *self = gFloaterWorldMap; - if (self->childHasKeyboardFocus("location") && - self->childGetValue("location").asString().length() > 0) + if (childHasKeyboardFocus("location") && + childGetValue("location").asString().length() > 0) { - self->setDefaultBtn("DoSearch"); + setDefaultBtn("DoSearch"); } else { - self->setDefaultBtn(NULL); + setDefaultBtn(NULL); } } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 03baa3eef..80cc25abd 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -151,8 +151,8 @@ protected: void flyToAvatar(); void teleportToAvatar(); - static void updateSearchEnabled( LLUICtrl* ctrl, void* userdata ); - static void onLocationFocusChanged( LLFocusableElement* ctrl, void* userdata ); + void updateSearchEnabled(); + void onLocationFocusChanged( LLFocusableElement* focus ); static void onLocationCommit( void* userdata ); static void onCommitLocation( LLUICtrl* ctrl, void* userdata ); static void onCommitSearchResult( LLUICtrl* ctrl, void* userdata ); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index bfe40f33c..6f638b695 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -35,13 +35,14 @@ #include "llfolderview.h" #include "llcallbacklist.h" -#include "llfloaterinventory.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. #include "llinventoryfilter.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" +#include "llinventorypanel.h" #include "llfoldertype.h" +#include "llfloaterinventory.h"// hacked in for the bonus context menu items. #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" @@ -79,7 +80,7 @@ ///---------------------------------------------------------------------------- const S32 RENAME_WIDTH_PAD = 4; -const S32 RENAME_HEIGHT_PAD = 6; +const S32 RENAME_HEIGHT_PAD = 1; const S32 AUTO_OPEN_STACK_DEPTH = 16; const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH + LLFolderViewItem::ICON_PAD @@ -88,6 +89,9 @@ const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH + /*first few characters*/ 40; const S32 MINIMUM_RENAMER_WIDTH = 80; +// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params. +const S32 STATUS_TEXT_HPAD = 6; +const S32 STATUS_TEXT_VPAD = 8; enum { SIGNAL_NO_KEYBOARD_FOCUS = 1, @@ -101,30 +105,6 @@ void copy_selected_item(void* user_data); void open_selected_items(void* user_data); void properties_selected_items(void* user_data); void paste_items(void* user_data); -void renamer_focus_lost( LLFocusableElement* handler, void* user_data ); - -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------------- @@ -195,40 +175,48 @@ void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) ///---------------------------------------------------------------------------- // Default constructor -LLFolderView::LLFolderView( const std::string& name, LLUIImagePtr root_folder_icon, - const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) : +LLFolderView::LLFolderView( const std::string& name, + const LLRect& rect, const LLUUID& source_id, LLPanel *parent_view, LLFolderViewEventListener* listener ) : #if LL_WINDOWS #pragma warning( push ) #pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list #endif - LLFolderViewFolder( name, root_folder_icon, this, NULL ), + LLFolderViewFolder( name, NULL, NULL, NULL, this, listener ), #if LL_WINDOWS #pragma warning( pop ) #endif + mRunningHeight(0), mScrollContainer( NULL ), mPopupMenuHandle(), mAllowMultiSelect(TRUE), + mShowEmptyMessage(TRUE), mShowFolderHierarchy(FALSE), mSourceID(source_id), mRenameItem( NULL ), mNeedsScroll( FALSE ), - mLastScrollItem( NULL ), + mUseLabelSuffix( TRUE ), + mPinningSelectedItem(FALSE), mNeedsAutoSelect( FALSE ), mAutoSelectOverride(FALSE), mNeedsAutoRename(FALSE), mDebugFilters(FALSE), mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mSearchType(1), mFilter( new LLInventoryFilter(name) ), mShowSelectionContext(FALSE), mShowSingleSelection(FALSE), mArrangeGeneration(0), - mUserData(NULL), - mSelectCallback(NULL), mSignalSelectCallback(0), mMinWidth(0), - mDragAndDropThisFrame(FALSE) + mDragAndDropThisFrame(FALSE), + mParentPanel(parent_view), + mUseEllipses(FALSE), + mDraggingOverItem(NULL), + mStatusTextBox(NULL), + mSearchType(1) { + mRoot = this; + + mShowLoadStatus = TRUE; LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); setRect( rect ); reshape(rect.getWidth(), rect.getHeight()); @@ -245,7 +233,7 @@ LLFolderView::LLFolderView( const std::string& name, LLUIImagePtr root_folder_ic // just make sure the label ("Inventory Folder") never shows up mLabel = LLStringUtil::null; - mRenamer = new LLLineEditor(std::string("ren"), getRect(), LLStringUtil::null, sFont, + mRenamer = new LLLineEditor(std::string("ren"), getRect(), LLStringUtil::null, getLabelFontForStyle(LLFontGL::NORMAL), DB_INV_ITEM_NAME_STR_LEN, &LLFolderView::commitRename, NULL, @@ -257,7 +245,17 @@ LLFolderView::LLFolderView( const std::string& name, LLUIImagePtr root_folder_ic mRenamer->setCommitOnFocusLost(TRUE); mRenamer->setVisible(FALSE); addChild(mRenamer); - + + LLFontGL* font = getLabelFontForStyle(mLabelStyle); + LLRect new_r = LLRect(rect.mLeft + ICON_PAD, + rect.mTop - TEXT_PAD, + rect.mRight, + rect.mTop - TEXT_PAD - llfloor(font->getLineHeight())); + mStatusTextBox = new LLTextBox(name, new_r, std::string(), font, false); + mStatusTextBox->setVisible(true); + mStatusTextBox->setHPad(STATUS_TEXT_HPAD); + mStatusTextBox->setVPad(STATUS_TEXT_VPAD); + mStatusTextBox->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP); // make the popup menu available LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_inventory.xml", parent_view); if (!menu) @@ -269,11 +267,15 @@ LLFolderView::LLFolderView( const std::string& name, LLUIImagePtr root_folder_ic mPopupMenuHandle = menu->getHandle(); setTabStop(TRUE); + + mListener->openItem(); } // Destroys the object LLFolderView::~LLFolderView( void ) { + closeRenamer(); + // The release focus call can potentially call the // scrollcontainer, which can potentially be called with a partly // destroyed scollcontainer. Just null it out here, and no worries @@ -282,22 +284,12 @@ LLFolderView::~LLFolderView( void ) mScrollContainer = NULL; mRenameItem = NULL; mRenamer = NULL; - gFocusMgr.releaseFocusIfNeeded( this ); - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } + mStatusTextBox = NULL; mAutoOpenItems.removeAllNodes(); gIdleCallbacks.deleteFunction(idle, this); - LLView::deleteViewByHandle(mPopupMenuHandle); - - if(mRenamer == gFocusMgr.getTopCtrl()) - { - gFocusMgr.setTopCtrl(NULL); - } + if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); mAutoOpenItems.removeAllNodes(); clearSelection(); @@ -315,18 +307,6 @@ BOOL LLFolderView::canFocusChildren() const return FALSE; } -void LLFolderView::checkTreeResortForModelChanged() -{ - if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME)) - { - // This is the case where something got added or removed. If we are date sorting - // everything including folders, then we need to rebuild the whole tree. - // Just set to something not SO_DATE to force the folder most resent date resort. - mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE; - setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE); - } -} - static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); void LLFolderView::setSortOrder(U32 order) @@ -334,15 +314,10 @@ void LLFolderView::setSortOrder(U32 order) if (order != mSortOrder) { LLFastTimer t(FTM_SORT); + mSortOrder = order; - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - + sortBy(order); arrangeAll(); } } @@ -417,10 +392,7 @@ BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) { mFolders.insert(mFolders.begin(), folder); } - if (folder->numSelected()) - { - recursiveIncrementNumDescendantsSelected(folder->numSelected()); - } + folder->setShowLoadStatus(mShowLoadStatus); folder->setOrigin(0, 0); folder->reshape(getRect().getWidth(), 0); folder->setVisible(FALSE); @@ -434,15 +406,16 @@ void LLFolderView::closeAllFolders() { // Close all the folders setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); + arrangeAll(); } -void LLFolderView::openFolder(const std::string& foldername) +void LLFolderView::openTopLevelFolders() { - LLFolderViewFolder* inv = getChild(foldername); - if (inv) + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) { - setSelection(inv, FALSE, FALSE); - inv->setOpen(TRUE); + folders_t::iterator fit = iter++; + (*fit)->setOpen(TRUE); } } @@ -459,6 +432,18 @@ static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); // This view grows and shinks to enclose all of its children items and folders. S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) { + if(!mScrollContainer) + return 1; + if (getListener()->getUUID().notNull()) + { + if (mNeedsSort) + { + mFolders.sort(mSortFunction); + mItems.sort(mSortFunction); + mNeedsSort = false; + } + } + LLFastTimer t2(FTM_ARRANGE); filter_generation = mFilter->getMinRequiredGeneration(); @@ -472,7 +457,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen getRoot()->getFilter()->getShowFolderState(); S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; + S32 running_height = mDebugFilters ? llceil(LLFontGL::getFontMonospace()->getLineHeight()) : 0; S32 target_height = running_height; S32 parent_item_height = getRect().getHeight(); @@ -490,6 +475,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter } + if (folderp->getVisible()) { S32 child_height = 0; @@ -528,19 +514,26 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen } } - S32 dummy_s32; - BOOL dummy_bool; - S32 min_width; - mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool); - reshape( llmax(min_width, total_width), running_height ); - - S32 new_min_width; - mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool); - if (new_min_width != min_width) + if(!mHasVisibleChildren)// is there any filtered items ? { - reshape( llmax(min_width, total_width), running_height ); + //Nope. We need to display status textbox, let's reserve some place for it + running_height = mStatusTextBox->getTextPixelHeight(); + target_height = running_height; } + mRunningHeight = running_height; + LLRect scroll_rect = mScrollContainer->getContentWindowRect(); + reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); + + LLRect new_scroll_rect = mScrollContainer->getContentWindowRect(); + if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) + { + reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); + } + + // move item renamer text field to item's new position + updateRenamerPosition(); + mTargetHeight = (F32)target_height; return llround(mTargetHeight); } @@ -571,15 +564,22 @@ void LLFolderView::filter( LLInventoryFilter& filter ) void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) { - S32 min_width = 0; - S32 dummy_height; - BOOL dummy_bool; + LLRect scroll_rect; if (mScrollContainer) { - mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool); + LLView::reshape(width, height, called_from_parent); + scroll_rect = mScrollContainer->getContentWindowRect(); } - width = llmax(mMinWidth, min_width); + width = llmax(mMinWidth, scroll_rect.getWidth()); + height = llmax(mRunningHeight, scroll_rect.getHeight()); + + // restrict width with scroll container's width + if (mUseEllipses) + width = scroll_rect.getWidth(); + LLView::reshape(width, height, called_from_parent); + + mReshapeSignal(mSelectedItems, FALSE); } void LLFolderView::addToSelectionList(LLFolderViewItem* item) @@ -637,6 +637,8 @@ LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) { + mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; + if( selection == this ) { return FALSE; @@ -644,7 +646,7 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, if( selection && take_keyboard_focus) { - setFocus(TRUE); + mParentPanel->setFocus(TRUE); } // clear selection down here because change of keyboard focus can potentially @@ -664,11 +666,33 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, llassert(mSelectedItems.size() <= 1); - mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; - return rv; } +void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) +{ + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getListener()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + } +} + +void LLFolderView::updateSelection() +{ + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +} + BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) { BOOL rv = FALSE; @@ -711,26 +735,6 @@ BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) return rv; } -void LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items) -{ - // now store resulting selection - if (mAllowMultiSelect) - { - LLFolderViewItem *cur_selection = getCurSelectedItem(); - LLFolderViewFolder::extendSelection(selection, cur_selection, items); - for (S32 i = 0; i < items.count(); i++) - { - addToSelectionList(items[i]); - } - } - else - { - setSelection(selection, FALSE, FALSE); - } - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; -} - static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection"); void LLFolderView::sanitizeSelection() { @@ -835,8 +839,7 @@ void LLFolderView::sanitizeSelection() } else { - // nothing selected to start with, so pick "My Inventory" as best guess - new_selection = getItemByID(gInventory.getRootFolderID()); + new_selection = NULL; } if (new_selection) @@ -848,23 +851,27 @@ void LLFolderView::sanitizeSelection() void LLFolderView::clearSelection() { - if (mSelectedItems.size() > 0) + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); + ++item_it) { - recursiveDeselect(FALSE); - mSelectedItems.clear(); + (*item_it)->setUnselected(); } + + mSelectedItems.clear(); + mSelectThisID.setNull(); } -BOOL LLFolderView::getSelectionList(std::set &selection) +std::set LLFolderView::getSelectionList() const { + std::set selection; for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { selection.insert((*item_it)->getListener()->getUUID()); } - - return (selection.size() != 0); + return selection; } BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) @@ -905,8 +912,8 @@ void LLFolderView::draw() { std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); - sSmallFont->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), + LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, + getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } @@ -916,10 +923,6 @@ void LLFolderView::draw() { closeAutoOpenedFolders(); } - if(this == gFocusMgr.getKeyboardFocus() && !getVisible()) - { - gFocusMgr.setKeyboardFocus( NULL ); - } // while dragging, update selection rendering to reflect single/multi drag status if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) @@ -949,22 +952,41 @@ void LLFolderView::draw() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) { mStatusText.clear(); + mStatusTextBox->setVisible( FALSE ); } - else + else if (mShowEmptyMessage) { + static LLCachedControl sSearchStatusColor(gColors, "InventorySearchStatusColor", LLColor4::white ); if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) { mStatusText = std::string("Searching..."); // *TODO:translate - sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } else { mStatusText = std::string("No matching items found in inventory."); // *TODO:translate - sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); + } + mStatusTextBox->setWrappedText(mStatusText); + mStatusTextBox->setVisible( TRUE ); + + // firstly reshape message textbox with current size. This is necessary to + // LLTextBox::getTextPixelHeight works properly + const LLRect local_rect = getLocalRect(); + mStatusTextBox->setShape(local_rect); + + // get preferable text height... + S32 pixel_height = mStatusTextBox->getTextPixelHeight(); + bool height_changed = local_rect.getHeight() != pixel_height; + if (height_changed) + { + // ... if it does not match current height, lets rearrange current view. + // This will indirectly call ::arrange and reshape of the status textbox. + // We should call this method to also notify parent about required rect. + // See EXT-7564, EXT-7047. + arrangeFromRoot(); } } - LLFolderViewFolder::draw(); + LLView::draw(); mDragAndDropThisFrame = FALSE; } @@ -980,17 +1002,7 @@ void LLFolderView::finishRenamingItem( void ) mRenameItem->rename( mRenamer->getText() ); } - mRenamer->setCommitOnFocusLost( FALSE ); - mRenamer->setFocus( FALSE ); - mRenamer->setVisible( FALSE ); - mRenamer->setCommitOnFocusLost( TRUE ); - gFocusMgr.setTopCtrl( NULL ); - - if( mRenameItem ) - { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; - } + closeRenamer(); // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. scrollToShowSelection(); @@ -998,18 +1010,40 @@ void LLFolderView::finishRenamingItem( void ) void LLFolderView::closeRenamer( void ) { - // will commit current name (which could be same as original name) - mRenamer->setFocus( FALSE ); - mRenamer->setVisible( FALSE ); - gFocusMgr.setTopCtrl( NULL ); - - if( mRenameItem ) + if (mRenamer && mRenamer->getVisible()) { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; + // Triggers onRenamerLost() that actually closes the renamer. + gFocusMgr.setTopCtrl( NULL ); } } +bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector& selectedItems) +{ + LLFolderViewItem* item_parent = dynamic_cast(item->getParent()); + + if (item_parent) + { + for(std::vector::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + const LLFolderViewItem* const selected_item = (*it); + + LLFolderViewItem* parent = item_parent; + + while (parent) + { + if (selected_item == parent) + { + return true; + } + + parent = dynamic_cast(parent->getParent()); + } + } + } + + return false; +} + void LLFolderView::removeSelectedItems( void ) { if(getVisible() && getEnabled()) @@ -1058,11 +1092,11 @@ void LLFolderView::removeSelectedItems( void ) // change selection on successful delete if (new_selection) { - setSelectionFromRoot(new_selection, new_selection->isOpen(), hasFocus()); + setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); } else { - setSelectionFromRoot(NULL, hasFocus()); + setSelectionFromRoot(NULL, mParentPanel->hasFocus()); } } } @@ -1081,18 +1115,18 @@ void LLFolderView::removeSelectedItems( void ) if (!new_selection) { new_selection = last_item->getPreviousOpenNode(FALSE); - while (new_selection && new_selection->isSelected()) + while (new_selection && (new_selection->isSelected() || isDescendantOfASelectedItem(new_selection, items))) { new_selection = new_selection->getPreviousOpenNode(FALSE); } } if (new_selection) { - setSelectionFromRoot(new_selection, new_selection->isOpen(), hasFocus()); + setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); } else { - setSelectionFromRoot(NULL, hasFocus()); + setSelectionFromRoot(NULL, mParentPanel->hasFocus()); } for(S32 i = 0; i < count; ++i) @@ -1186,9 +1220,20 @@ void LLFolderView::propertiesSelectedItems( void ) } } +void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type) +{ + LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get(); + + if (!folder_bridge) return; + LLViewerInventoryCategory *cat = folder_bridge->getCategory(); + if (!cat) return; + cat->changeType(new_folder_type); +} void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) { - if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) + if ((mAutoOpenItems.check() == item) || + (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) || + item->isOpen()) { return; } @@ -1207,7 +1252,9 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); item->setOpen(TRUE); - scrollToShowItem(item); + LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); + scrollToShowItem(item, constraint_rect); } void LLFolderView::closeAutoOpenedFolders() @@ -1298,12 +1345,43 @@ void LLFolderView::copy() BOOL LLFolderView::canCut() const { - return FALSE; + if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) + { + return FALSE; + } + + for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) + { + const LLFolderViewItem* item = *selected_it; + const LLFolderViewEventListener* listener = item->getListener(); + + if (!listener || !listener->isItemRemovable()) + { + return FALSE; + } + } + return TRUE; } void LLFolderView::cut() { - // implement Windows-style cut-and-leave + // clear the inventory clipboard + LLInventoryClipboard::instance().reset(); + S32 count = mSelectedItems.size(); + if(getVisible() && getEnabled() && (count > 0)) + { + LLFolderViewEventListener* listener = NULL; + selected_items_t::iterator item_it; + for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + listener = (*item_it)->getListener(); + if(listener) + { + listener->cutToClipboard(); + } + } + } + mSearchString.clear(); } BOOL LLFolderView::canPaste() const @@ -1386,47 +1464,19 @@ void LLFolderView::startRenamingSelectedItem( void ) { mRenameItem = item; - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation(); - S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2); - item->localPointToScreen( x, y, &x, &y ); - screenPointToLocal( x, y, &x, &y ); - mRenamer->setOrigin( x, y ); + updateRenamerPosition(); - S32 scroller_height = 0; - S32 scroller_width = gViewerWindow->getWindowWidth(); - BOOL dummy_bool; - if (mScrollContainer) - { - mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool); - } - - S32 width = llmax(llmin(item->getRect().getWidth() - x, scroller_width - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); - S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD); - mRenamer->reshape( width, height, TRUE ); mRenamer->setText(item->getName()); mRenamer->selectAll(); mRenamer->setVisible( TRUE ); // set focus will fail unless item is visible mRenamer->setFocus( TRUE ); - mRenamer->setLostTopCallback(onRenamerLost); + mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); gFocusMgr.setTopCtrl( mRenamer ); } } -void LLFolderView::setFocus(BOOL focus) -{ - if (focus) - { - if(!hasFocus()) - { - gEditMenuHandler = this; - } - } - - LLFolderViewFolder::setFocus(focus); -} - BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { BOOL handled = FALSE; @@ -1539,10 +1589,26 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { if (next == last_selected) { + //special case for LLAccordionCtrl + if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed + { + clearSelection(); + return TRUE; + } return FALSE; } setSelection( next, FALSE, TRUE ); } + else + { + //special case for LLAccordionCtrl + if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed + { + clearSelection(); + return TRUE; + } + return FALSE; + } } scrollToShowSelection(); mSearchString.clear(); @@ -1587,6 +1653,13 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { if (prev == this) { + // If case we are in accordion tab notify parent to go to the previous accordion + if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed + { + clearSelection(); + return TRUE; + } + return FALSE; } setSelection( prev, FALSE, TRUE ); @@ -1629,7 +1702,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) break; } - if (!handled && hasFocus()) + if (!handled && mParentPanel->hasFocus()) { if (key == KEY_BACKSPACE) { @@ -1661,7 +1734,7 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) } BOOL handled = FALSE; - if (hasFocus()) + if (mParentPanel->hasFocus()) { // SL-51858: Key presses are not being passed to the Popup menu. // A proper fix is non-trivial so instead just close the menu. @@ -1718,20 +1791,13 @@ BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) mKeyboardSelection = FALSE; mSearchString.clear(); - setFocus(TRUE); + mParentPanel->setFocus(TRUE); + + LLEditMenuHandler::gEditMenuHandler = this; return LLView::handleMouseDown( x, y, mask ); } -void LLFolderView::onFocusLost( ) -{ - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - LLUICtrl::onFocusLost(); -} - BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) { // get first selected item @@ -1808,35 +1874,20 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) { // all user operations move keyboard focus to inventory // this way, we know when to stop auto-updating a search - setFocus(TRUE); + mParentPanel->setFocus(TRUE); BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; S32 count = mSelectedItems.size(); LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if(handled && (count > 0) && menu) + if ( handled + && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible + && menu ) { - //menu->empty(); - const LLView::child_list_t *list = menu->getChildList(); - - LLView::child_list_t::const_iterator menu_itor; - for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) - { - (*menu_itor)->setVisible(TRUE); - (*menu_itor)->setEnabled(TRUE); - } - - // Successively filter out invalid options - selected_items_t::iterator item_itor; - U32 flags = FIRST_SELECTED_ITEM; - for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor) - { - (*item_itor)->buildContextMenu(*menu, flags); - flags = 0x0; - } - - menu->arrange(); + updateMenuOptions(menu); + menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(this, menu, x, y); + //menu->needsArrange(); // update menu height if needed } else { @@ -1849,6 +1900,37 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) return handled; } +// Add "--no options--" if the menu is completely blank. +BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const +{ + const std::string nooptions_str = "--no options--"; + LLView *nooptions_item = NULL; + + const LLView::child_list_t *list = menu->getChildList(); + for (LLView::child_list_t::const_iterator itor = list->begin(); + itor != list->end(); + ++itor) + { + LLView *menu_item = (*itor); + if (menu_item->getVisible()) + { + return FALSE; + } + std::string name = menu_item->getName(); + if (menu_item->getName() == nooptions_str) + { + nooptions_item = menu_item; + } + } + if (nooptions_item) + { + nooptions_item->setVisible(TRUE); + nooptions_item->setEnabled(FALSE); + return TRUE; + } + return FALSE; +} + BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) { return LLView::handleHover( x, y, mask ); @@ -1861,9 +1943,28 @@ BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, std::string& tooltip_msg) { mDragAndDropThisFrame = TRUE; + // have children handle it first BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + // when drop is not handled by child, it should be handled + // by the folder which is the hierarchy root. + if (!handled) + { + if (getListener()->getUUID().notNull()) + { + handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + else + { + if (!mFolders.empty()) + { + // dispatch to last folder as a hack to support "Contents" folder in object inventory + handled = mFolders.back()->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); + } + } + } + if (handled) { lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; @@ -1883,21 +1984,27 @@ BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLFolderView::deleteAllChildren() { - if(mRenamer == gFocusMgr.getTopCtrl()) - { - gFocusMgr.setTopCtrl(NULL); - } - LLView::deleteViewByHandle(mPopupMenuHandle); + closeRenamer(); + if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); mPopupMenuHandle = LLHandle(); - mRenamer = NULL; + mScrollContainer = NULL; mRenameItem = NULL; + mRenamer = NULL; + mStatusTextBox = NULL; + clearSelection(); LLView::deleteAllChildren(); } void LLFolderView::scrollToShowSelection() { - if (mSelectedItems.size()) + // If items are filtered while background fetch is in progress + // scrollbar resets to the first filtered item. See EXT-3981. + // However we allow scrolling for folder views with mAutoSelectOverride + // (used in Places SP) as an exception because the selection in them + // is not reset during items filtering. See STORM-133. + if ( (!LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mAutoSelectOverride) + && mSelectedItems.size() ) { mNeedsScroll = TRUE; } @@ -1905,49 +2012,41 @@ void LLFolderView::scrollToShowSelection() // If the parent is scroll containter, scroll it to make the selection // is maximally visible. -void LLFolderView::scrollToShowItem(LLFolderViewItem* item) +void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect) { + if (!mScrollContainer) return; + // don't scroll to items when mouse is being used to scroll/drag and drop if (gFocusMgr.childHasMouseCapture(mScrollContainer)) { mNeedsScroll = FALSE; return; } - if(item && mScrollContainer) + + // if item exists and is in visible portion of parent folder... + if(item) { - LLRect local_rect = item->getRect(); + LLRect local_rect = item->getLocalRect(); LLRect item_scrolled_rect; // item position relative to display area of scroller + LLRect visible_doc_rect = mScrollContainer->getVisibleContentRect(); S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = llround(sFont->getLineHeight()); - // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder - S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); - item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer); - item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer); + S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); + // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder + S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); + + // get portion of item that we want to see... + LLRect item_local_rect = LLRect(item->getIndentation(), + local_rect.getHeight(), + llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()), + llmax(0, local_rect.getHeight() - max_height_to_show)); - item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight); - LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, - mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1); + LLRect item_doc_rect; - S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight(); - if (item != mLastScrollItem || // if we're scrolling to focus on a new item - // or the item has just appeared on screen and it wasn't onscreen before - (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && - (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset))) - { - // we now have a position on screen that we want to keep stable - // offset of selection relative to top of visible area - mLastScrollOffset = scroll_offset; - mLastScrollItem = item; - } + item->localRectToOtherView(item_local_rect, &item_doc_rect, this); - mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset ); + mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); - // after scrolling, store new offset - // in case we don't have room to maintain the original position - LLCoordGL new_item_left_top; - item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer); - mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1); } } @@ -1997,7 +2096,7 @@ LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) { LLFastTimer _(FTM_GET_ITEM_BY_ID); - if (id.isNull()) + if (id == getListener()->getUUID()) { return this; } @@ -2012,14 +2111,40 @@ LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) return NULL; } +LLFolderViewFolder* LLFolderView::getFolderByID(const LLUUID& id) +{ + if (id == getListener()->getUUID()) + { + return this; + } + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end(); + ++iter) + { + LLFolderViewFolder *folder = (*iter); + if (folder->getListener()->getUUID() == id) + { + return folder; + } + } + return NULL; +} static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select"); static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory"); -extern std::set sFolderViewItems; //dumb hack // Main idle routine void LLFolderView::doIdle() { + // If this is associated with the user's inventory, don't do anything + // until that inventory is loaded up. + const LLInventoryPanel *inventory_panel = dynamic_cast(mParentPanel); + if (inventory_panel && !inventory_panel->getIsViewsInitialized()) + { + return; + } + LLFastTimer t2(FTM_INVENTORY); BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); @@ -2048,27 +2173,77 @@ void LLFolderView::doIdle() LLFastTimer t3(FTM_AUTO_SELECT); // select new item only if a filtered item not currently selected LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if (selected_itemp != NULL && sFolderViewItems.count(selected_itemp) == 0) - { - // There is a crash bug due to a race condition: when a folder view item is - // destroyed, its address may still appear in mSelectedItems a couple of doIdle() - // later, even if you explicitely clear this list and dirty the filters in the - // destructor... - // This code avoids the crash bug. - llwarns << "Invalid folder view item (" << selected_itemp << ") in selection: clearing the latter." << llendl; - dirtyFilter(); - clearSelection(); - requestArrange(); - } - else if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->getFiltered())) + if ((selected_itemp && !selected_itemp->getFiltered()) && !mAutoSelectOverride) { // select first filtered item LLSelectFirstFilteredItem filter; applyFunctorRecursively(filter); } + + // Open filtered folders for folder views with mAutoSelectOverride=TRUE. + // Used by LLPlacesFolderView. + if (mAutoSelectOverride && !mFilter->getFilterSubString().empty()) + { + LLOpenFilteredFolders filter; + applyFunctorRecursively(filter); + } + scrollToShowSelection(); } + // during filtering process, try to pin selected item's location on screen + // this will happen when searching your inventory and when new items arrive + if (filter_modified_and_active) + { + // calculate rectangle to pin item to at start of animated rearrange + if (!mPinningSelectedItem && !mSelectedItems.empty()) + { + // lets pin it! + mPinningSelectedItem = TRUE; + + LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); + LLFolderViewItem* selected_item = mSelectedItems.back(); + + LLRect item_rect; + selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); + // if item is visible in scrolled region + if (visible_content_rect.overlaps(item_rect)) + { + // then attempt to keep it in same place on screen + mScrollConstraintRect = item_rect; + mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); + } + else + { + // otherwise we just want it onscreen somewhere + LLRect content_rect = mScrollContainer->getContentWindowRect(); + mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + } + } + } + else + { + // stop pinning selected item after folders stop rearranging + if (!needsArrange()) + { + mPinningSelectedItem = FALSE; + } + } + + LLRect constraint_rect; + if (mPinningSelectedItem) + { + // use last known constraint rect for pinned item + constraint_rect = mScrollConstraintRect; + } + else + { + // during normal use (page up/page down, etc), just try to fit item on screen + LLRect content_rect = mScrollContainer->getContentWindowRect(); + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + } + + BOOL is_visible = isInVisibleChain(); if ( is_visible ) @@ -2082,20 +2257,20 @@ void LLFolderView::doIdle() if (mSelectedItems.size() && mNeedsScroll) { - scrollToShowItem(mSelectedItems.back()); + scrollToShowItem(mSelectedItems.back(), constraint_rect); // continue scrolling until animated layout change is done - if (getCompletedFilterGeneration() >= mFilter->getMinRequiredGeneration() && - (!needsArrange() || !is_visible)) + if (!filter_modified_and_active + && (!needsArrange() || !is_visible)) { mNeedsScroll = FALSE; } } - if (mSignalSelectCallback && mSelectCallback) + if (mSignalSelectCallback) { //RN: we use keyboard focus as a proxy for user-explicit actions BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectCallback(mSelectedItems, take_keyboard_focus, mUserData); + mSelectSignal(mSelectedItems, take_keyboard_focus); } mSignalSelectCallback = FALSE; } @@ -2111,7 +2286,6 @@ void LLFolderView::idle(void* user_data) } } - void LLFolderView::dumpSelectionInformation() { llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; @@ -2124,15 +2298,167 @@ void LLFolderView::dumpSelectionInformation() llinfos << "****************************************" << llendl; } +void LLFolderView::updateRenamerPosition() +{ + if(mRenameItem) + { + // See also LLFolderViewItem::draw() + S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); + S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; + mRenameItem->localPointToScreen( x, y, &x, &y ); + screenPointToLocal( x, y, &x, &y ); + mRenamer->setOrigin( x, y ); + + LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidthScaled(), 0); + if (mScrollContainer) + { + scroller_rect = mScrollContainer->getContentWindowRect(); + } + + S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); + S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; + mRenamer->reshape( width, height, TRUE ); + } +} + +// Update visibility and availability (i.e. enabled/disabled) of context menu items. +void LLFolderView::updateMenuOptions(LLMenuGL* menu) +{ + const LLView::child_list_t *list = menu->getChildList(); + + LLView::child_list_t::const_iterator menu_itor; + for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) + { + (*menu_itor)->setVisible(FALSE); + (*menu_itor)->pushVisible(TRUE); + (*menu_itor)->setEnabled(TRUE); + } + + // Successively filter out invalid options + + U32 flags = FIRST_SELECTED_ITEM; + for (selected_items_t::iterator item_itor = mSelectedItems.begin(); + item_itor != mSelectedItems.end(); + ++item_itor) + { + LLFolderViewItem* selected_item = (*item_itor); + selected_item->buildContextMenu(*menu, flags); + flags = 0x0; + } + + addNoOptions(menu); +} + +// Refresh the context menu (that is already shown). +void LLFolderView::updateMenu() +{ + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); + if (menu && menu->getVisible()) + { + updateMenuOptions(menu); + menu->needsArrange(); // update menu height if needed + } +} + +bool LLFolderView::selectFirstItem() +{ + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();++iter) + { + LLFolderViewFolder* folder = (*iter ); + if (folder->getVisible()) + { + LLFolderViewItem* itemp = folder->getNextFromChild(0,true); + if(itemp) + setSelection(itemp,FALSE,TRUE); + return true; + } + + } + for(items_t::iterator iit = mItems.begin(); + iit != mItems.end(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + if (itemp->getVisible()) + { + setSelection(itemp,FALSE,TRUE); + return true; + } + } + return false; +} +bool LLFolderView::selectLastItem() +{ + for(items_t::reverse_iterator iit = mItems.rbegin(); + iit != mItems.rend(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + if (itemp->getVisible()) + { + setSelection(itemp,FALSE,TRUE); + return true; + } + } + for (folders_t::reverse_iterator iter = mFolders.rbegin(); + iter != mFolders.rend();++iter) + { + LLFolderViewFolder* folder = (*iter); + if (folder->getVisible()) + { + LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true); + if(itemp) + setSelection(itemp,FALSE,TRUE); + return true; + } + } + return false; +} + + +S32 LLFolderView::notify(const LLSD& info) +{ + if(info.has("action")) + { + std::string str_action = info["action"]; + if(str_action == "select_first") + { + setFocus(true); + selectFirstItem(); + scrollToShowSelection(); + return 1; + + } + else if(str_action == "select_last") + { + setFocus(true); + selectLastItem(); + scrollToShowSelection(); + return 1; + } + } + return 0; +} + + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- - -//static -void LLFolderView::onRenamerLost( LLUICtrl* renamer, void* user_data) +void LLFolderView::onRenamerLost() { - renamer->setVisible(FALSE); + if (mRenamer && mRenamer->getVisible()) + { + mRenamer->setVisible(FALSE); + + // will commit current name (which could be same as original name) + mRenamer->setFocus(FALSE); + } + + if( mRenameItem ) + { + setSelectionFromRoot( mRenameItem, TRUE ); + mRenameItem = NULL; + } } LLInventoryFilter* LLFolderView::getFilter() @@ -2150,9 +2476,9 @@ bool LLFolderView::getFilterWorn() const return mFilter->getFilterWorn(); } -U32 LLFolderView::getFilterTypes() const +U32 LLFolderView::getFilterObjectTypes() const { - return mFilter->getFilterTypes(); + return mFilter->getFilterObjectTypes(); } PermissionMask LLFolderView::getFilterPermissions() const @@ -2209,26 +2535,3 @@ void properties_selected_items(void* user_data) fv->propertiesSelectedItems(); } } - -///---------------------------------------------------------------------------- -/// Class LLFolderViewEventListener -///---------------------------------------------------------------------------- - -void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus, - BOOL set_selection, - BOOL take_keyboard_focus) -{ - if(!focus) return; - LLFolderView* root = focus->getRoot(); - focus->getParentFolder()->requestArrange(); - if(set_selection) - { - focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus); - if(root) - { - root->scrollToShowSelection(); - } - } -} - - diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 98e40455d..ebb0cb1aa 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -71,18 +71,14 @@ class LLTextBox; // manages the screen region of the folder view. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLUICtrl; -class LLLineEditor; - -class LLFolderView : public LLFolderViewFolder, LLEditMenuHandler +class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler { public: typedef void (*SelectCallback)(const std::deque &items, BOOL user_action, void* data); - static F32 sAutoOpenTime; - LLFolderView( const std::string& name, LLUIImagePtr root_folder_icon, const LLRect& rect, - const LLUUID& source_id, LLView *parent_view ); + LLFolderView( const std::string& name, const LLRect& rect, + const LLUUID& source_id, LLPanel *parent_view, LLFolderViewEventListener* listener ); virtual ~LLFolderView( void ); virtual BOOL canFocusChildren() const; @@ -92,28 +88,31 @@ public: // FolderViews default to sort by name. This will change that, // and resort the items if necessary. void setSortOrder(U32 order); - void checkTreeResortForModelChanged(); void setFilterPermMask(PermissionMask filter_perm_mask); - void setSelectCallback(SelectCallback callback, void* user_data) { mSelectCallback = callback, mUserData = user_data; } + + typedef boost::signals2::signal& items, BOOL user_action)> signal_t; + void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); } + void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); } void setAllowMultiSelect(BOOL allow) { mAllowMultiSelect = allow; } LLInventoryFilter* getFilter(); const std::string getFilterSubString(BOOL trim = FALSE); bool getFilterWorn() const; - U32 getFilterTypes() const; + U32 getFilterObjectTypes() const; PermissionMask getFilterPermissions() const; // *NOTE: use getFilter()->getShowFolderState(); //LLInventoryFilter::EFolderShow getShowFolderState(); U32 getSortOrder() const; BOOL isFilterModified(); - BOOL getAllowMultiSelect() { return mAllowMultiSelect; } + + bool getAllowMultiSelect() { return mAllowMultiSelect; } U32 toggleSearchType(std::string toggle); U32 getSearchType() const; // Close all folders in the view void closeAllFolders(); - void openFolder(const std::string& foldername); + void openTopLevelFolders(); virtual void toggleOpen() {}; virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse); @@ -136,13 +135,17 @@ public: virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); + // Used by menu callbacks + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + + // Called once a frame to update the selection if mSelectThisID has been set + void updateSelection(); + // This method is used to toggle the selection of an item. Walks // children, and keeps track of selected objects. virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - virtual void extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items); - - virtual BOOL getSelectionList(std::set &selection); + virtual std::set getSelectionList() const; // make sure if ancestor is selected, descendents are not void sanitizeSelection(); @@ -152,6 +155,8 @@ public: BOOL startDrag(LLToolDragAndDrop::ESource source); void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } + void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } + LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } // deletion functionality void removeSelectedItems(); @@ -160,6 +165,9 @@ public: void openSelectedItems( void ); void propertiesSelectedItems( void ); + // change the folder type + void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type); + void autoOpenItem(LLFolderViewFolder* item); void closeAutoOpenedFolders(); BOOL autoOpenTest(LLFolderViewFolder* item); @@ -188,9 +196,6 @@ public: //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - // LLUICtrl Functionality - /*virtual*/ void setFocus(BOOL focus); - // LLView functionality ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); @@ -205,13 +210,12 @@ public: EAcceptance* accept, std::string& tooltip_msg); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void onFocusLost(); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void draw(); virtual void deleteAllChildren(); void scrollToShowSelection(); - void scrollToShowItem(LLFolderViewItem* item); + void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); void setScrollContainer( LLScrollableContainerView* parent ) { mScrollContainer = parent; } LLRect getVisibleRect(); @@ -221,10 +225,12 @@ public: void setShowSingleSelection(BOOL show); BOOL getShowSingleSelection() { return mShowSingleSelection; } F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } + bool getUseEllipses() { return mUseEllipses; } void addItemID(const LLUUID& id, LLFolderViewItem* itemp); void removeItemID(const LLUUID& id); LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); void doIdle(); // Real idle routine static void idle(void* user_data); // static glue to doIdle() @@ -232,21 +238,37 @@ public: BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; } BOOL needsAutoRename() { return mNeedsAutoRename; } void setNeedsAutoRename(BOOL val) { mNeedsAutoRename = val; } + void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; } + void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; } BOOL getDebugFilters() { return mDebugFilters; } + LLPanel* getParentPanel() { return mParentPanel; } // DEBUG only void dumpSelectionInformation(); + virtual S32 notify(const LLSD& info) ; + + bool useLabelSuffix() { return mUseLabelSuffix; } + void updateMenu(); + +private: + void updateMenuOptions(LLMenuGL* menu); + void updateRenamerPosition(); + protected: LLScrollableContainerView* mScrollContainer; // NULL if this is not a child of a scroll container. static void commitRename( LLUICtrl* renamer, void* user_data ); - static void onRenamerLost( LLUICtrl* renamer, void* user_data); + void onRenamerLost(); void finishRenamingItem( void ); void closeRenamer( void ); + bool selectFirstItem(); + bool selectLastItem(); + + BOOL addNoOptions(LLMenuGL* menu) const; protected: LLHandle mPopupMenuHandle; @@ -254,6 +276,7 @@ protected: selected_items_t mSelectedItems; BOOL mKeyboardSelection; BOOL mAllowMultiSelect; + BOOL mShowEmptyMessage; BOOL mShowFolderHierarchy; LLUUID mSourceID; @@ -262,11 +285,12 @@ protected: LLLineEditor* mRenamer; BOOL mNeedsScroll; - LLFolderViewItem* mLastScrollItem; - LLCoordGL mLastScrollOffset; + BOOL mPinningSelectedItem; + LLRect mScrollConstraintRect; BOOL mNeedsAutoSelect; BOOL mAutoSelectOverride; BOOL mNeedsAutoRename; + bool mUseLabelSuffix; BOOL mDebugFilters; U32 mSortOrder; @@ -282,12 +306,32 @@ protected: LLFrameTimer mMultiSelectionFadeTimer; S32 mArrangeGeneration; - void* mUserData; - SelectCallback mSelectCallback; + signal_t mSelectSignal; + signal_t mReshapeSignal; S32 mSignalSelectCallback; S32 mMinWidth; + S32 mRunningHeight; std::map mItemMap; BOOL mDragAndDropThisFrame; + + LLUUID mSelectThisID; // if non null, select this item + + LLPanel* mParentPanel; + + /** + * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. + * NOTE: For now it uses only to cut LLFolderViewItem::mLabel text to be used for Landmarks in Places Panel. + */ + bool mUseEllipses; // See EXT-719 + + /** + * Contains item under mouse pointer while dragging + */ + LLFolderViewItem* mDraggingOverItem; // See EXT-719 + +public: + static F32 sAutoOpenTime; + LLTextBox* mStatusTextBox; }; diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index deefa298b..3bfbf3611 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -55,23 +55,27 @@ public: virtual const LLUUID& getUUID() const = 0; virtual time_t getCreationDate() const = 0; // UTC seconds virtual PermissionMask getPermissionMask() const = 0; + virtual LLFolderType::EType getPreferredType() const = 0; virtual LLPointer getIcon() const = 0; + virtual LLPointer getOpenIcon() const { return getIcon(); } virtual LLFontGL::StyleFlags getLabelStyle() const = 0; virtual std::string getLabelSuffix() const = 0; virtual void openItem( void ) = 0; + virtual void closeItem( void ) = 0; virtual void previewItem( void ) = 0; virtual void selectItem(void) = 0; virtual void showProperties(void) = 0; virtual BOOL isItemRenameable() const = 0; virtual BOOL renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) = 0; // Can be moved to another folder - virtual BOOL isItemRemovable( void ) = 0; // Can be destroyed + virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder + virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed + virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. virtual BOOL removeItem() = 0; virtual void removeBatch(LLDynamicArray& batch) = 0; virtual void move( LLFolderViewEventListener* parent_listener ) = 0; virtual BOOL isItemCopyable() const = 0; virtual BOOL copyToClipboard() const = 0; - virtual BOOL cutToClipboard() const = 0; + virtual void cutToClipboard() = 0; virtual BOOL isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; virtual void pasteLinkFromClipboard() = 0; @@ -79,12 +83,13 @@ public: virtual BOOL isUpToDate() const = 0; virtual BOOL hasChildren() const = 0; virtual LLInventoryType::EType getInventoryType() const = 0; - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) {} - + virtual void performAction(LLInventoryModel* model, std::string action) = 0; + virtual LLWearableType::EType getWearableType() const = 0; + // This method should be called when a drag begins. returns TRUE // if the drag can begin, otherwise FALSE. virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; - + // This method will be called to determine if a drop can be // performed, and will set drop to TRUE if a drop is // requested. Returns TRUE if a drop is possible/happened, @@ -92,17 +97,6 @@ public: virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data) = 0; - - // This method is called when the object being referenced by the - // bridge is actually dropped. This allows for cleanup of the old - // view, reference counting, etc. -// virtual void dropped() = 0; - - // this method accesses the parent and arranges and sets it as - // specified. - void arrangeAndSet(LLFolderViewItem* focus, BOOL set_selection, - BOOL take_keyboard_focus = TRUE); }; - #endif diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 044529577..ae48372e3 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -44,38 +44,36 @@ #include "llfocusmgr.h" // gFocusMgr #include "lltrans.h" // statics -const LLFontGL* LLFolderViewItem::sFont = NULL; -const LLFontGL* LLFolderViewItem::sSmallFont = NULL; +std::map LLFolderViewItem::sFonts; // map of styles to fonts +// only integers can be initialized in header const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f; -LLColor4 LLFolderViewItem::sFgColor; -LLColor4 LLFolderViewItem::sHighlightBgColor; -LLColor4 LLFolderViewItem::sHighlightFgColor; -LLColor4 LLFolderViewItem::sFilterBGColor; -LLColor4 LLFolderViewItem::sFilterTextColor; -LLColor4 LLFolderViewItem::sSuffixColor; -LLColor4 LLFolderViewItem::sSearchStatusColor; LLUIImagePtr LLFolderViewItem::sArrowImage; LLUIImagePtr LLFolderViewItem::sBoxImage; -// This is used to keep track of existing folder view items and -// avoid a crash bug due to a race condition (see in doIdle()). -std::set sFolderViewItems; + +//static +LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) +{ + LLFontGL* rtn = sFonts[style]; + if (!rtn) // grab label font with this style, lazily + { + LLFontDescriptor labelfontdesc("SansSerif", "Small", style); + rtn = LLFontGL::getFont(labelfontdesc); + if (!rtn) + { + rtn = LLFontGL::getFontMonospace(); + } + sFonts[style] = rtn; + } + return rtn; +} //static void LLFolderViewItem::initClass() { - sFont = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ); - sSmallFont = LLResMgr::getInstance()->getRes( LLFONT_SMALL ); - sFgColor = gColors.getColor( "MenuItemEnabledColor" ); - sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" ); - sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" ); - sFilterBGColor = gColors.getColor( "FilterBackgroundColor" ); - sFilterTextColor = gColors.getColor( "FilterTextColor" ); - sSuffixColor = gColors.getColor( "InventoryItemSuffixColor" ); - sSearchStatusColor = gColors.getColor( "InventorySearchStatusColor" ); sArrowImage = LLUI::getUIImage("folder_arrow.tga"); sBoxImage = LLUI::getUIImage("rounded_square.tga"); } @@ -83,44 +81,58 @@ void LLFolderViewItem::initClass() //static void LLFolderViewItem::cleanupClass() { + sFonts.clear(); sArrowImage = NULL; sBoxImage = NULL; } // Default constructor // NOTE: Optimize this, we call it a *lot* when opening a large inventory LLFolderViewItem::LLFolderViewItem( const std::string& name, LLUIImagePtr icon, + LLUIImagePtr icon_open, + LLUIImagePtr icon_overlay, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener ) : LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT), - mLabel( name ), mLabelWidth(0), - mCreationDate(creation_date), + mLabelWidthDirty(false), mParentFolder( NULL ), - mListener( listener ), mIsSelected( FALSE ), mIsCurSelection( FALSE ), mSelectPending(FALSE), mLabelStyle( LLFontGL::NORMAL ), - mIcon(icon), mHasVisibleChildren(FALSE), mIndentation(0), + mItemHeight(16 + ICON_PAD), mPassedFilter(FALSE), mLastFilterGeneration(-1), mStringMatchOffset(std::string::npos), mControlLabelRotation(0.f), - mRoot( root ), mDragAndDropTarget(FALSE), - mIsLoading(FALSE) + mIsLoading(FALSE), + mLabel( name ), + mRoot( root ), + mCreationDate(creation_date), + mIcon(icon), + mIconOpen(icon_open), + mIconOverlay(icon_overlay), + mListener(listener), + mShowLoadStatus(true), + mSearchType(0) { - sFolderViewItems.insert(this); - refresh(); // possible opt: only call refreshFromListener() - setTabStop(FALSE); + postBuild();//Not parsing xml file yet. } + +BOOL LLFolderViewItem::postBuild() +{ + refresh(); + setTabStop(FALSE); + return TRUE; +} + // Destroys the object LLFolderViewItem::~LLFolderViewItem( void ) { - sFolderViewItems.erase(this); delete mListener; mListener = NULL; } @@ -151,7 +163,7 @@ LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) { return NULL; } - + LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); while(itemp && !itemp->getVisible()) { @@ -173,7 +185,7 @@ LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) { return NULL; } - + LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); // Skip over items that are invisible or are hidden from the UI. @@ -222,8 +234,6 @@ void LLFolderViewItem::setIcon(LLUIImagePtr icon) mIcon = icon; } - - // refresh information from the listener void LLFolderViewItem::refreshFromListener() { @@ -232,44 +242,56 @@ void LLFolderViewItem::refreshFromListener() mLabel = mListener->getDisplayName(); setIcon(mListener->getIcon()); time_t creation_date = mListener->getCreationDate(); - if (mCreationDate != creation_date) + if ((creation_date > 0) && (mCreationDate != creation_date)) { - mCreationDate = mListener->getCreationDate(); + setCreationDate(creation_date); dirtyFilter(); } - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); - - LLInventoryItem* item = gInventory.getItem(mListener->getUUID()); - - std::string desc; - if (item) + if (mRoot->useLabelSuffix()) { - if (!item->getDescription().empty()) - { - desc = item->getDescription(); - LLStringUtil::toUpper(desc); - } + mLabelStyle = mListener->getLabelStyle(); + mLabelSuffix = mListener->getLabelSuffix(); } - mSearchableLabelDesc = desc; - - std::string creator_name; - if (item) - { - if (item->getCreatorUUID().notNull()) - { - gCacheName->getFullName(item->getCreatorUUID(), creator_name); - LLStringUtil::toUpper(creator_name); - } - } - mSearchableLabelCreator = creator_name; + + updateExtraSearchCriteria(); } } +void LLFolderViewItem::updateExtraSearchCriteria() +{ + if(!mListener) + return; + LLInventoryItem* item = gInventory.getItem(mListener->getUUID()); + + std::string desc; + if (item) + { + if (!item->getDescription().empty()) + { + desc = item->getDescription(); + LLStringUtil::toUpper(desc); + } + } + mSearchableLabelDesc = desc; + + std::string creator_name; + if (item) + { + if (item->getCreatorUUID().notNull()) + { + gCacheName->getFullName(item->getCreatorUUID(), creator_name); + LLStringUtil::toUpper(creator_name); + } + } + mSearchableLabelCreator = creator_name; + + updateSearchLabelType(); +} + void LLFolderViewItem::refresh() { refreshFromListener(); - + std::string searchable_label(mLabel); searchable_label.append(mLabelSuffix); LLStringUtil::toUpper(searchable_label); @@ -277,21 +299,17 @@ void LLFolderViewItem::refresh() if (mSearchableLabel.compare(searchable_label)) { mSearchableLabel.assign(searchable_label); + updateSearchLabelType(); dirtyFilter(); - // some part of label has changed, so overall width has potentially changed + // some part of label has changed, so overall width has potentially changed, and sort order too if (mParentFolder) { + mParentFolder->requestSort(); mParentFolder->requestArrange(); } } - S32 label_width = sFont->getWidth(mLabel); - if( mLabelSuffix.size() ) - { - label_width += sFont->getWidth( mLabelSuffix ); - } - - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width; + mLabelWidthDirty = true; } void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) @@ -318,7 +336,31 @@ void LLFolderViewItem::arrangeFromRoot() S32 height = 0; S32 width = 0; - root->arrange( &width, &height, 0 ); + S32 total_height = root->arrange( &width, &height, 0 ); + + LLSD params; + params["action"] = "size_changes"; + params["height"] = total_height; + getParent()->notifyParent(params); +} + +// Utility function for LLFolderView +void LLFolderViewItem::arrangeAndSet(BOOL set_selection, + BOOL take_keyboard_focus) +{ + LLFolderView* root = getRoot(); + if (getParentFolder()) + { + getParentFolder()->requestArrange(); + } + if(set_selection) + { + setSelectionFromRoot(this, TRUE, take_keyboard_focus); + if(root) + { + root->scrollToShowSelection(); + } + } } // This function clears the currently selected item, and records the @@ -338,11 +380,10 @@ void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL getRoot()->changeSelection(selection, selected); } -void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection) +std::set LLFolderViewItem::getSelectionList() const { - LLDynamicArray selected_items; - - getRoot()->extendSelection(selection, NULL, selected_items); + std::set selection; + return selection; } EInventorySortGroup LLFolderViewItem::getSortGroup() const @@ -367,34 +408,51 @@ BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* roo // makes sure that this view and it's children are the right size. S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) { - mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0; + S32 indentation = LEFT_INDENTATION; + // Only indent deeper items in hierarchy + mIndentation = (getParentFolder() + && getParentFolder()->getParentFolder() ) + ? mParentFolder->getIndentation() + indentation + : 0; + if (mLabelWidthDirty) + { + mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mSearchableLabel); + mLabelWidthDirty = false; + } + *width = llmax(*width, mLabelWidth + mIndentation); + + // determine if we need to use ellipses to avoid horizontal scroll. EXT-719 + bool use_ellipses = getRoot()->getUseEllipses(); + if (use_ellipses) + { + // limit to set rect to avoid horizontal scrollbar + *width = llmin(*width, getRoot()->getRect().getWidth()); + } *height = getItemHeight(); return *height; } S32 LLFolderViewItem::getItemHeight() { - S32 icon_height = mIcon->getHeight(); - S32 label_height = llround(sFont->getLineHeight()); - return llmax( icon_height, label_height ) + ICON_PAD; + return mItemHeight; } void LLFolderViewItem::filter( LLInventoryFilter& filter) { - BOOL filtered = mListener && filter.check(this); - - // if our visibility will change as a result of this filter, then + const BOOL previous_passed_filter = mPassedFilter; + const BOOL passed_filter = filter.check(this); + + // If our visibility will change as a result of this filter, then // we need to be rearranged in our parent folder - if (getVisible() != filtered) + if (mParentFolder) { - if (mParentFolder) - { + if (getVisible() != passed_filter + || previous_passed_filter != passed_filter ) mParentFolder->requestArrange(); - } } - setFiltered(filtered, filter.getCurrentGeneration()); + setFiltered(passed_filter, filter.getCurrentGeneration()); mStringMatchOffset = filter.getStringMatchOffset(); filter.decrementFilterCount(); @@ -424,10 +482,6 @@ BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, if (selection == this && !mIsSelected) { selectItem(); - if(mListener) - { - mListener->selectItem(); - } } else if (mIsSelected) // Deselect everything else. { @@ -438,9 +492,9 @@ BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected) { - if (selection == this && mIsSelected != selected) + if (selection == this) { - if (mIsSelected) + if (mIsSelected) { deselectItem(); } @@ -448,10 +502,6 @@ BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selecte { selectItem(); } - if(mListener) - { - mListener->selectItem(); - } return TRUE; } return FALSE; @@ -459,29 +509,18 @@ BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selecte void LLFolderViewItem::deselectItem(void) { - llassert(mIsSelected); - mIsSelected = FALSE; - - // Update ancestors' count of selected descendents. - LLFolderViewFolder* parent_folder = getParentFolder(); - if (parent_folder) - { - parent_folder->recursiveIncrementNumDescendantsSelected(-1); - } } void LLFolderViewItem::selectItem(void) { - llassert(!mIsSelected); - - mIsSelected = TRUE; - - // Update ancestors' count of selected descendents. - LLFolderViewFolder* parent_folder = getParentFolder(); - if (parent_folder) + if (mIsSelected == FALSE) { - parent_folder->recursiveIncrementNumDescendantsSelected(1); + if (mListener) + { + mListener->selectItem(); + } + mIsSelected = TRUE; } } @@ -534,8 +573,6 @@ BOOL LLFolderViewItem::remove() return TRUE; } - - // Build an appropriate context menu for the item. void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) { @@ -572,21 +609,19 @@ void LLFolderViewItem::rename(const std::string& new_name) if(mParentFolder) { - mParentFolder->resort(this); + mParentFolder->requestSort(); } } } } -std::string& LLFolderViewItem::getSearchableLabel() +void LLFolderViewItem::updateSearchLabelType() { - mSearchable = ""; - U32 flags = mRoot->getSearchType(); - if (flags == 0 || (flags & 1)) - { + mSearchType = mRoot->getSearchType(); + mSearchable.erase(); + if (!mSearchType || mSearchType & 1) mSearchable = mSearchableLabel; - } - if (flags & 2) + if (mSearchType & 2) { if (mSearchable.length()) { @@ -594,7 +629,7 @@ std::string& LLFolderViewItem::getSearchableLabel() } mSearchable += mSearchableLabelDesc; } - if (flags & 4) + if (mSearchType & 4) { if (mSearchable.length()) { @@ -602,6 +637,12 @@ std::string& LLFolderViewItem::getSearchableLabel() } mSearchable += mSearchableLabelCreator; } +} + +const std::string& LLFolderViewItem::getSearchableLabel() +{ + if(mSearchType != mRoot->getSearchType()) + updateSearchLabelType(); return mSearchable; } @@ -633,6 +674,11 @@ BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) { + if (LLView::childrenHandleMouseDown(x, y, mask)) + { + return TRUE; + } + // No handler needed for focus lost since this class has no // state that depends on it. gFocusMgr.setMouseCapture( this ); @@ -645,7 +691,7 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) } else if (mask & MASK_SHIFT) { - extendSelectionFromRoot(this); + getParentFolder()->extendSelectionTo(this); } else { @@ -752,6 +798,11 @@ BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks) BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) { + if (LLView::childrenHandleMouseUp(x, y, mask)) + { + return TRUE; + } + // if mouse hasn't moved since mouse down... if ( pointInView(x, y) && mSelectPending ) { @@ -762,7 +813,7 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) } else if (mask & MASK_SHIFT) { - extendSelectionFromRoot(this); + getParentFolder()->extendSelectionTo(this); } else { @@ -804,7 +855,10 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, } if(mParentFolder && !handled) { + // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event. + mRoot->setDraggingOverItem(this); handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); + mRoot->setDraggingOverItem(NULL); } if (handled) { @@ -817,35 +871,46 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFolderViewItem::draw() { - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... - (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter) + static LLCachedControl sFgColor(gColors, "MenuItemEnabledColor", LLColor4::white ); + static LLCachedControl sHighlightBgColor(gColors, "MenuItemHighlightBgColor", LLColor4::white ); + static LLCachedControl sHighlightFgColor(gColors, "MenuItemHighlightFgColor", LLColor4::white ); + static LLCachedControl sFilterBGColor(gColors, "FilterBackgroundColor", LLColor4::white ); + static LLCachedControl sFilterTextColor(gColors, "FilterTextColor", LLColor4::white ); + static LLCachedControl sSuffixColor(gColors, "InventoryItemSuffixColor", LLColor4::white ); + static LLCachedControl sSearchStatusColor(gColors, "InventorySearchStatusColor", LLColor4::white ); + + const S32 TOP_PAD = 4; + const S32 FOCUS_LEFT = 1; + const LLFontGL* font = getLabelFontForStyle(mLabelStyle); + + const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID()); + const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID()); + + //--------------------------------------------------------------------------------// + // Draw open folder arrow + // + const bool up_to_date = mListener && mListener->isUpToDate(); + const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) // we fetched our children and some of them have passed the filter... + || (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter) + if (possibly_has_children && sArrowImage) { - possibly_has_children = true; - } - if(/*mControlLabel[0] != '\0' && */possibly_has_children) - { - if (sArrowImage) - { - gl_draw_scaled_rotated_image(mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, sArrowImage->getImage(), sFgColor); - } + gl_draw_scaled_rotated_image( + mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, + ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, sArrowImage->getImage(), sFgColor); } - F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); - - // If we have keyboard focus, draw selection filled - BOOL show_context = getRoot()->getShowSelectionContext(); - BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot()); - - // always render "current" item, only render other selected items if - // mShowSingleSelection is FALSE - if( mIsSelected ) + //--------------------------------------------------------------------------------// + // Draw highlight for selected items + // + const BOOL show_context = getRoot()->getShowSelectionContext(); + const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled + const S32 focus_top = getRect().getHeight(); + const S32 focus_bottom = getRect().getHeight() - mItemHeight; + const bool folder_open = (getRect().getHeight() > mItemHeight + 4); + if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLColor4 bg_color = sHighlightBgColor; - //const S32 TRAILING_PAD = 5; // It just looks better with this. if (!mIsCurSelection) { // do time-based fade of extra objects @@ -861,135 +926,173 @@ void LLFolderViewItem::draw() bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); } } - - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - bg_color, filled); + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + bg_color, filled); if (mIsCurSelection) { - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, sHighlightFgColor, FALSE); } - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) + if (folder_open) { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, sHighlightFgColor, FALSE); if (show_context) { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, TRUE); + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, + getRect().getWidth() - 2, + 0, + sHighlightBgColor, TRUE); } } } + + //--------------------------------------------------------------------------------// + // Draw DragNDrop highlight + // if (mDragAndDropTarget) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d( - 0, - getRect().getHeight(), - getRect().getWidth() - 2, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD), - sHighlightBgColor, FALSE); - - if (getRect().getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2) + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + sHighlightBgColor, FALSE); + if (folder_open) { - gl_rect_2d( - 0, - llfloor(getRect().getHeight() - sFont->getLineHeight() - ICON_PAD) - 2, - getRect().getWidth() - 2, - 2, - sHighlightBgColor, FALSE); + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, + sHighlightBgColor, FALSE); } mDragAndDropTarget = FALSE; } - - if(mIcon) + const LLViewerInventoryItem *item = getInventoryItem(); + const BOOL highlight_link = mIconOverlay && item && item->getIsLinkType(); + //--------------------------------------------------------------------------------// + // Draw open icon + // + const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; + if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders + { + mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); + } + else if (mIcon) { - mIcon->draw(mIndentation + ARROW_SIZE + TEXT_PAD, getRect().getHeight() - mIcon->getHeight()); + mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + } + + if (highlight_link) + { + mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); } - if (!mLabel.empty()) + //--------------------------------------------------------------------------------// + // Exit if no label to draw + // + if (mLabel.empty()) { - // highlight filtered text - BOOL debug_filters = getRoot()->getDebugFilters(); - LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor ); - F32 right_x; - F32 y = (F32)getRect().getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; + return; + } + LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; + F32 right_x = 0; + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; + F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); - if (debug_filters) + //--------------------------------------------------------------------------------// + // Highlight filtered text + // + if (getRoot()->getDebugFilters()) + { + if (!getFiltered() && !possibly_has_children) { - if (!getFiltered() && !possibly_has_children) - { - color.mV[VALPHA] *= 0.5f; - } - - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? + color.mV[VALPHA] *= 0.5f; + } + LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f); - sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; - } + LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x, FALSE ); + text_left = right_x; + } + //--------------------------------------------------------------------------------// + // Draw the actual label text + // + font->renderUTF8(mLabel, 0, text_left, y, color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); + //--------------------------------------------------------------------------------// + // Draw "Loading..." text + // + bool root_is_loading = false; + if (in_inventory) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); + } + if (in_library) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); + } + if ((mIsLoading + && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) + || (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() + && root_is_loading + && mShowLoadStatus)) + { + std::string load_string = " ( Loading... ) "; + font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x, FALSE); + } - if ( mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime") ) + //--------------------------------------------------------------------------------// + // Draw label suffix + // + if (!mLabelSuffix.empty()) + { + font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x, FALSE ); + } + + //--------------------------------------------------------------------------------// + // Highlight string match + // + if (sBoxImage.notNull() && mStringMatchOffset != std::string::npos) + { + // don't draw backgrounds for zero-length strings + S32 filter_string_length = getRoot()->getFilterSubString().size(); + if (filter_string_length > 0 && (mRoot->getSearchType() & 1)) { - // *TODO: Translate - sFont->renderUTF8( std::string("Loading... "), 0, text_left, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE); - text_left = right_x; - } - - sFont->renderUTF8( mLabel, 0, text_left, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - if (!mLabelSuffix.empty()) - { - sFont->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - } - - if (sBoxImage.notNull() && mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings std::string combined_string = mLabel + mLabelSuffix; - S32 filter_string_length = getRoot()->getFilterSubString().size(); - std::string combined_string_upper = combined_string; - LLStringUtil::toUpper(combined_string_upper); - if (filter_string_length > 0 && (mRoot->getSearchType() & 1) && - combined_string_upper.find(mRoot->getFilterSubString()) == mStringMatchOffset) - { - S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - sFont->getLineHeight() - 3); - S32 top = getRect().getHeight(); + S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; + S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; + S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); + S32 top = getRect().getHeight() - TOP_PAD; - LLRect box_rect(left, top, right, bottom); - sBoxImage->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 y = (F32)getRect().getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD; - sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } + LLUIImage* box_image = sBoxImage; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; + font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, + + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + filter_string_length, S32_MAX, &right_x, FALSE ); } } @@ -1006,10 +1109,11 @@ void LLFolderViewItem::draw() // Default constructor LLFolderViewFolder::LLFolderViewFolder( const std::string& name, LLUIImagePtr icon, + LLUIImagePtr icon_open, + LLUIImagePtr icon_link, LLFolderView* root, LLFolderViewEventListener* listener ): - LLFolderViewItem( name, icon, 0, root, listener ), // 0 = no create time - mNumDescendantsSelected(0), + LLFolderViewItem( name, icon, icon_open, icon_link, 0, root, listener ), // 0 = no create time mIsOpen(FALSE), mExpanderHighlighted(FALSE), mCurHeight(0.f), @@ -1020,12 +1124,12 @@ LLFolderViewFolder::LLFolderViewFolder( const std::string& name, LLUIImagePtr ic mLastArrangeGeneration( -1 ), mLastCalculatedWidth(0), mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1) + mMostFilteredDescendantGeneration(-1), + mNeedsSort(false), + mPassedFolderFilter(FALSE) { - mType = std::string("(folder)"); } - // Destroys the object LLFolderViewFolder::~LLFolderViewFolder( void ) { @@ -1034,6 +1138,17 @@ LLFolderViewFolder::~LLFolderViewFolder( void ) gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() } +void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) +{ + mPassedFolderFilter = filtered; + mLastFilterGeneration = filter_generation; +} + +bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) +{ + return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); +} + // addToFolder() returns TRUE if it succeeds. FALSE otherwise BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) { @@ -1046,13 +1161,19 @@ BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* r return folder->addFolder(this); } -// Finds width and height of this object and it's children. Also -// makes sure that this view and it's children are the right size. +// Finds width and height of this object and its children. Also +// makes sure that this view and its children are the right size. S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) { + // sort before laying out contents + if (mNeedsSort) + { + mFolders.sort(mSortFunction); + mItems.sort(mSortFunction); + mNeedsSort = false; + } + mHasVisibleChildren = hasFilteredDescendants(filter_generation); - - LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getFilter()->getShowFolderState(); // calculate height as a single item (without any children), and reshapes rectangle to match LLFolderViewItem::arrange( width, height, filter_generation ); @@ -1085,8 +1206,10 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) } else { - folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter + folderp->setVisible( folderp->getListener() + && (folderp->getFiltered(filter_generation) + || (folderp->getFilteredFolder(filter_generation) + && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter } if (folderp->getVisible()) @@ -1146,12 +1269,12 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) if (llabs(mCurHeight - mTargetHeight) > 1.f) { mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); - + requestArrange(); // hide child elements that fall out of current animated height for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; // number of pixels that bottom of folder label is from top of parent folder @@ -1164,7 +1287,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; // number of pixels that bottom of item label is from top of parent folder @@ -1194,6 +1317,13 @@ BOOL LLFolderViewFolder::needsArrange() return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); } +void LLFolderViewFolder::requestSort() +{ + mNeedsSort = true; + // whenever item order changes, we need to lay things out again + requestArrange(); +} + void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) { mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); @@ -1214,6 +1344,8 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) // you will automatically fail this time, so we only // check against items that have passed the filter S32 must_pass_generation = filter.getMustPassGeneration(); + + bool autoopen_folders = (filter.hasFilterString()); // if we have already been filtered against this generation, skip out if (getCompletedFilterGeneration() >= filter_generation) @@ -1230,9 +1362,11 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) // go ahead and flag this folder as done mLastFilterGeneration = filter_generation; } - else + else // filter self only on first pass through { - // filter self only on first pass through + // filter against folder rules + filterFolder(filter); + // and then item rules LLFolderViewItem::filter( filter ); } } @@ -1290,10 +1424,7 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration())) { mMostFilteredDescendantGeneration = filter_generation; - if (mRoot->needsAutoSelect()) - { - folder->setOpenArrangeRecursively(TRUE); - } + requestArrange(); } // just skip it, it has already been filtered continue; @@ -1306,7 +1437,8 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation)) { mMostFilteredDescendantGeneration = filter_generation; - if (getRoot()->needsAutoSelect()) + requestArrange(); + if (getRoot()->needsAutoSelect() && autoopen_folders) { folder->setOpenArrangeRecursively(TRUE); } @@ -1327,6 +1459,7 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) if (item->getFiltered()) { mMostFilteredDescendantGeneration = filter_generation; + requestArrange(); } continue; } @@ -1345,9 +1478,10 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) if (item->getFiltered(filter.getMinRequiredGeneration())) { mMostFilteredDescendantGeneration = filter_generation; + requestArrange(); } } - + // if we didn't use all filter iterations // that means we filtered all of our descendants // instead of exhausting the filter count for this frame @@ -1358,6 +1492,31 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) } } +void LLFolderViewFolder::filterFolder(LLInventoryFilter& filter) +{ + const BOOL previous_passed_filter = mPassedFolderFilter; + const BOOL passed_filter = filter.checkFolder(this); + + // If our visibility will change as a result of this filter, then + // we need to be rearranged in our parent folder + if (mParentFolder) + { + if (getVisible() != passed_filter + || previous_passed_filter != passed_filter ) + { + mParentFolder->requestArrange(); + } + } + + setFilteredFolder(passed_filter, filter.getCurrentGeneration()); + filter.decrementFilterCount(); + + if (getRoot()->getDebugFilters()) + { + mStatusText = llformat("%d", mLastFilterGeneration); + } +} + void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) { // if this folder is now filtered, but wasn't before @@ -1379,56 +1538,54 @@ void LLFolderViewFolder::dirtyFilter() LLFolderViewItem::dirtyFilter(); } +BOOL LLFolderViewFolder::getFiltered() +{ + return getFilteredFolder(getRoot()->getFilter()->getMinRequiredGeneration()) + && LLFolderViewItem::getFiltered(); +} + +BOOL LLFolderViewFolder::getFiltered(S32 filter_generation) +{ + return getFilteredFolder(filter_generation) && LLFolderViewItem::getFiltered(filter_generation); +} + +BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation) +{ + return mMostFilteredDescendantGeneration >= filter_generation; +} + + BOOL LLFolderViewFolder::hasFilteredDescendants() { return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); } -void LLFolderViewFolder::recursiveIncrementNumDescendantsSelected(S32 increment) -{ - LLFolderViewFolder* parent_folder = this; - do - { - parent_folder->mNumDescendantsSelected += increment; - - // Make sure we don't have negative values. - llassert(parent_folder->mNumDescendantsSelected >= 0); - - parent_folder = parent_folder->getParentFolder(); - } - while(parent_folder); -} - // Passes selection information on to children and record selection // information if necessary. BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) + BOOL take_keyboard_focus) { BOOL rv = FALSE; - if( selection == this ) + if (selection == this) { - if (!isSelected()) + if (!isSelected()) { selectItem(); } - if(mListener) - { - mListener->selectItem(); - } rv = TRUE; } else { - if (isSelected()) + if (isSelected()) { deselectItem(); } rv = FALSE; } BOOL child_selected = FALSE; - + for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) @@ -1438,7 +1595,7 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem } } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) @@ -1466,35 +1623,31 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selec if (isSelected() != selected) { rv = TRUE; - if (selected) + if (selected) { selectItem(); } else { - deselectItem(); + deselectItem(); } } - if(mListener && selected) - { - mListener->selectItem(); - } } for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; - if ((*fit)->changeSelection(selection, selected)) + if((*fit)->changeSelection(selection, selected)) { rv = TRUE; } } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; - if ((*iit)->changeSelection(selection, selected)) + if((*iit)->changeSelection(selection, selected)) { rv = TRUE; } @@ -1502,158 +1655,265 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selec return rv; } -void LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& selected_items) +LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse) { - // pass on to child folders first - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL; + + std::deque item_a_ancestors; + + LLFolderViewFolder* parent = item_a->getParentFolder(); + while(parent) { - folders_t::iterator fit = iter++; - (*fit)->extendSelection(selection, last_selected, selected_items); + item_a_ancestors.push_back(parent); + parent = parent->getParentFolder(); } - // handle selection of our immediate children... - BOOL reverse_select = FALSE; - BOOL found_last_selected = FALSE; - BOOL found_selection = FALSE; - LLDynamicArray items_to_select; - LLFolderViewItem* item; - - //...folders first... - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + std::deque item_b_ancestors; + + parent = item_b->getParentFolder(); + while(parent) { - folders_t::iterator fit = iter++; - item = (*fit); - if(item == selection) - { - found_selection = TRUE; - } - else if (item == last_selected) - { - found_last_selected = TRUE; - if (found_selection) - { - reverse_select = TRUE; - } - } + item_b_ancestors.push_back(parent); + parent = parent->getParentFolder(); + } - if (found_selection || found_last_selected) - { - // deselect currently selected items so they can be pushed back on queue - if (item->isSelected()) - { - item->changeSelection(item, FALSE); - } - items_to_select.put(item); - } + LLFolderViewFolder* common_ancestor = item_a->getRoot(); - if (found_selection && found_last_selected) + while(item_a_ancestors.size() > item_b_ancestors.size()) + { + item_a = item_a_ancestors.front(); + item_a_ancestors.pop_front(); + } + + while(item_b_ancestors.size() > item_a_ancestors.size()) + { + item_b = item_b_ancestors.front(); + item_b_ancestors.pop_front(); + } + + while(item_a_ancestors.size()) + { + common_ancestor = item_a_ancestors.front(); + + if (item_a_ancestors.front() == item_b_ancestors.front()) { + // which came first, sibling a or sibling b? + for (folders_t::iterator it = common_ancestor->mFolders.begin(), end_it = common_ancestor->mFolders.end(); + it != end_it; + ++it) + { + LLFolderViewItem* item = *it; + + if (item == item_a) + { + reverse = false; + return common_ancestor; + } + if (item == item_b) + { + reverse = true; + return common_ancestor; + } + } + + for (items_t::iterator it = common_ancestor->mItems.begin(), end_it = common_ancestor->mItems.end(); + it != end_it; + ++it) + { + LLFolderViewItem* item = *it; + + if (item == item_a) + { + reverse = false; + return common_ancestor; + } + if (item == item_b) + { + reverse = true; + return common_ancestor; + } + } break; - } + } + + item_a = item_a_ancestors.front(); + item_a_ancestors.pop_front(); + item_b = item_b_ancestors.front(); + item_b_ancestors.pop_front(); } - if (!(found_selection && found_last_selected)) + return NULL; +} + +void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector& items) +{ + bool selecting = start == NULL; + if (reverse) { - //,,,then items - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend(); + it != end_it; + ++it) { - items_t::iterator iit = iter++; - item = (*iit); - if(item == selection) + if (*it == end) { - found_selection = TRUE; + return; } - else if (item == last_selected) + if (selecting) { - found_last_selected = TRUE; - if (found_selection) - { - reverse_select = TRUE; - } + items.push_back(*it); } - if (found_selection || found_last_selected) + if (*it == start) { - // deselect currently selected items so they can be pushed back on queue - if (item->isSelected()) - { - item->changeSelection(item, FALSE); - } - items_to_select.put(item); + selecting = true; + } + } + for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend(); + it != end_it; + ++it) + { + if (*it == end) + { + return; } - if (found_selection && found_last_selected) + if (selecting) { - break; + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; } } } - - if (found_last_selected && found_selection) + else { - // we have a complete selection inside this folder - for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0; - reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++) + for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end(); + it != end_it; + ++it) { - LLFolderViewItem* item = items_to_select[index]; - if (item->changeSelection(item, TRUE)) + if (*it == end) { - selected_items.put(item); + return; + } + + if (selecting) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; } } - } - else if (found_selection) - { - // last selection was not in this folder....go ahead and select just the new item - if (selection->changeSelection(selection, TRUE)) + for (items_t::iterator it = mItems.begin(), end_it = mItems.end(); + it != end_it; + ++it) { - selected_items.put(selection); + if (*it == end) + { + return; + } + + if (selecting) + { + items.push_back(*it); + } + + if (*it == start) + { + selecting = true; + } } } } -void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self) +void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) { - if (isSelected() && deselect_self) + if (getRoot()->getAllowMultiSelect() == FALSE) return; + + LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); + if (cur_selected_item == NULL) { - deselectItem(); + cur_selected_item = new_selection; } - if (0 == mNumDescendantsSelected) + + bool reverse = false; + LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse); + if (!common_ancestor) return; + + LLFolderViewItem* last_selected_item_from_cur = cur_selected_item; + LLFolderViewFolder* cur_folder = cur_selected_item->getParentFolder(); + + std::vector items_to_select_forward; + + while(cur_folder != common_ancestor) { - return; + cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward); + + last_selected_item_from_cur = cur_folder; + cur_folder = cur_folder->getParentFolder(); } - // Deselect all items in this folder. - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + std::vector items_to_select_reverse; + + LLFolderViewItem* last_selected_item_from_new = new_selection; + cur_folder = new_selection->getParentFolder(); + while(cur_folder != common_ancestor) { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); + cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse); + + last_selected_item_from_new = cur_folder; + cur_folder = cur_folder->getParentFolder(); + } + + common_ancestor->gatherChildRangeExclusive(last_selected_item_from_cur, last_selected_item_from_new, reverse, items_to_select_forward); + + for (std::vector::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend(); + it != end_it; + ++it) + { + items_to_select_forward.push_back(*it); + } + + LLFolderView* root = getRoot(); + + for (std::vector::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end(); + it != end_it; + ++it) + { + LLFolderViewItem* item = *it; if (item->isSelected()) { - item->deselectItem(); + root->removeFromSelectionList(item); } + else + { + item->selectItem(); + } + root->addToSelectionList(item); } - // Recursively deselect all folders in this folder. - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + if (new_selection->isSelected()) { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folder = (*fit); - folder->recursiveDeselect(TRUE); + root->removeFromSelectionList(new_selection); } - + else + { + new_selection->selectItem(); + } + root->addToSelectionList(new_selection); } + void LLFolderViewFolder::destroyView() { for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; LLFolderViewItem* item = (*iit); @@ -1669,8 +1929,8 @@ void LLFolderViewFolder::destroyView() folderp->destroyView(); // removes entry from mFolders } - deleteAllChildren(); - + //deleteAllChildren(); + if (mParentFolder) { mParentFolder->removeView(this); @@ -1683,8 +1943,6 @@ BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) { if(item->remove()) { - //RN: this seem unneccessary as remove() moves to trash - //removeView(item); return TRUE; } return FALSE; @@ -1721,21 +1979,13 @@ void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) LLFolderViewFolder* f = static_cast(item); folders_t::iterator ft; ft = std::find(mFolders.begin(), mFolders.end(), f); - if(ft != mFolders.end()) + if (ft != mFolders.end()) { - if ((*ft)->numSelected()) - { - recursiveIncrementNumDescendantsSelected(-(*ft)->numSelected()); - } mFolders.erase(ft); } } else { - if ((*it)->isSelected()) - { - recursiveIncrementNumDescendantsSelected(-1); - } mItems.erase(it); } //item has been removed, need to update filter @@ -1746,14 +1996,6 @@ void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) removeChild(item); } -// This function is called by a child that needs to be resorted. -// This is only called for renaming an object because it won't work for date -void LLFolderViewFolder::resort(LLFolderViewItem* item) -{ - mItems.sort(mSortFunction); - mFolders.sort(mSortFunction); -} - bool LLFolderViewFolder::isTrash() const { if (mAmTrash == LLFolderViewFolder::UNKNOWN) @@ -1771,9 +2013,9 @@ void LLFolderViewFolder::sortBy(U32 order) return; } - // Propegate this change to sub folders + // Propagate this change to sub folders for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; (*fit)->sortBy(order); @@ -1789,7 +2031,7 @@ void LLFolderViewFolder::sortBy(U32 order) if (order & LLInventoryFilter::SO_DATE) { time_t latest = 0; - + if (!mItems.empty()) { LLFolderViewItem* item = *(mItems.begin()); @@ -1831,15 +2073,14 @@ EInventorySortGroup LLFolderViewFolder::getSortGroup() const return SG_TRASH_FOLDER; } - // Folders that can't be moved are 'system' folders. if( mListener ) { - if( !(mListener->isItemMovable()) ) + if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType())) { return SG_SYSTEM_FOLDER; } } - + return SG_NORMAL_FOLDER; } @@ -1853,7 +2094,7 @@ BOOL LLFolderViewFolder::isMovable() } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; if(!(*iit)->isMovable()) @@ -1863,7 +2104,7 @@ BOOL LLFolderViewFolder::isMovable() } for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; if(!(*fit)->isMovable()) @@ -1886,7 +2127,7 @@ BOOL LLFolderViewFolder::isRemovable() } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; if(!(*iit)->isRemovable()) @@ -1896,7 +2137,7 @@ BOOL LLFolderViewFolder::isRemovable() } for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; if(!(*fit)->isRemovable()) @@ -1911,38 +2152,54 @@ BOOL LLFolderViewFolder::isRemovable() // this is an internal method used for adding items to folders. BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) { - - items_t::iterator it = std::lower_bound( - mItems.begin(), - mItems.end(), - item, - mSortFunction); - mItems.insert(it,item); - if (item->isSelected()) - { - recursiveIncrementNumDescendantsSelected(1); - } + mItems.push_back(item); + item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); item->setVisible(FALSE); - addChild( item ); + + addChild(item); + item->dirtyFilter(); + + // Update the folder creation date if the folder has no creation date + bool setting_date = false; + const time_t item_creation_date = item->getCreationDate(); + if ((item_creation_date > 0) && (mCreationDate == 0)) + { + setCreationDate(item_creation_date); + setting_date = true; + } + + // Handle sorting requestArrange(); + requestSort(); + + // Traverse parent folders and update creation date and resort, if necessary + LLFolderViewFolder* parentp = getParentFolder(); + while (parentp) + { + // Update the parent folder creation date + if (setting_date && (parentp->mCreationDate == 0)) + { + parentp->setCreationDate(item_creation_date); + } + + if (parentp->mSortFunction.isByDate()) + { + // parent folder doesn't have a time stamp yet, so get it from us + parentp->requestSort(); + } + + parentp = parentp->getParentFolder(); + } + return TRUE; } // this is an internal method used for adding items to folders. BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) { - folders_t::iterator it = std::lower_bound( - mFolders.begin(), - mFolders.end(), - folder, - mSortFunction); - mFolders.insert(it,folder); - if (folder->numSelected()) - { - recursiveIncrementNumDescendantsSelected(folder->numSelected()); - } + mFolders.push_back(folder); folder->setOrigin(0, 0); folder->reshape(getRect().getWidth(), 0); folder->setVisible(FALSE); @@ -1950,6 +2207,14 @@ BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) folder->dirtyFilter(); // rearrange all descendants too, as our indentation level might have changed folder->requestArrange(TRUE); + requestSort(); + LLFolderViewFolder* parentp = getParentFolder(); + while (parentp && !parentp->mSortFunction.isByDate()) + { + // parent folder doesn't have a time stamp yet, so get it from us + parentp->requestSort(); + parentp = parentp->getParentFolder(); + } return TRUE; } @@ -1988,18 +2253,22 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r { BOOL was_open = mIsOpen; mIsOpen = openitem; - if(!was_open && openitem) + if (mListener) { - if(mListener) + if(!was_open && openitem) { mListener->openItem(); } + else if(was_open && !openitem) + { + mListener->closeItem(); + } } if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) { for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ @@ -2019,11 +2288,11 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r } BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) + BOOL drop, + EDragAndDropType c_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) { BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data); if (accepted) @@ -2047,18 +2316,34 @@ void LLFolderViewFolder::openItem( void ) toggleOpen(); } +void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor) +{ + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + functor.doItem((*fit)); + } + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + functor.doItem((*iit)); + } +} + void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) { functor.doFolder(this); for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; (*fit)->applyFunctorRecursively(functor); } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; functor.doItem((*iit)); @@ -2069,13 +2354,13 @@ void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFun { functor(mListener); for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) + iter != mFolders.end();) { folders_t::iterator fit = iter++; (*fit)->applyListenerFunctorRecursively(functor); } for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) + iter != mItems.end();) { items_t::iterator iit = iter++; (*iit)->applyListenerFunctorRecursively(functor); @@ -2090,33 +2375,16 @@ BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - LLFolderView* root_view = getRoot(); - BOOL handled = FALSE; - if(mIsOpen) + + if (mIsOpen) { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; + handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); } if (!handled) { - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data); - - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - if (!drop && accepted) - { - root_view->autoOpenTest(this); - } + handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; } @@ -2124,6 +2392,33 @@ BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, return TRUE; } +BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, + BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data); + + if (accepted) + { + mDragAndDropTarget = TRUE; + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + + if (!drop && accepted) + { + getRoot()->autoOpenTest(this); + } + + return TRUE; +} + BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) { @@ -2131,7 +2426,7 @@ BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) // fetch contents of this folder, as context menu can depend on contents // still, user would have to open context menu again to see the changes gInventory.fetchDescendentsOf(mListener->getUUID()); - + if( mIsOpen ) { handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; @@ -2154,12 +2449,6 @@ BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) handled = LLFolderViewItem::handleHover(x, y, mask); } - //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > getRect().getHeight() - ) - //{ - // gViewerWindow->setCursor(UI_CURSOR_ARROW); - // mExpanderHighlighted = TRUE; - // handled = TRUE; - //} return handled; } @@ -2229,22 +2518,24 @@ void LLFolderViewFolder::draw() bool possibly_has_children = false; bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date && mListener && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) + if(!up_to_date + && mListener + && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) { possibly_has_children = true; } - - + + BOOL loading = (mIsOpen && possibly_has_children && !up_to_date ); - + if ( loading && !mIsLoading ) { // Measure how long we've been in the loading state mTimeSinceRequestStart.reset(); } - + mIsLoading = loading; LLFolderViewItem::draw(); @@ -2349,7 +2640,7 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, // turn on downwards traversal for next folder ++fit; } - + if (fit != fend) { result = (*fit); @@ -2379,7 +2670,6 @@ LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, return result; } - // this does postfix traversal, as folders are listed above their contents LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) { @@ -2447,7 +2737,7 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it { ++iit; } - + if (iit != iend) { // we found an appropriate item @@ -2486,6 +2776,7 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it return result; } + bool LLInventorySort::updateSort(U32 order) { if (order != mSortOrder) @@ -2501,12 +2792,40 @@ bool LLInventorySort::updateSort(U32 order) bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) { + /* TO-DO + // ignore sort order for landmarks in the Favorites folder. + // they should be always sorted as in Favorites bar. See EXT-719 + if (a->getSortGroup() == SG_ITEM + && b->getSortGroup() == SG_ITEM + && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK + && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + + static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); + LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); + + if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) + { + // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem + // or to LLInvFVBridge + LLViewerInventoryItem* aitem = (static_cast(a->getListener()))->getItem(); + LLViewerInventoryItem* bitem = (static_cast(b->getListener()))->getItem(); + if (!aitem || !bitem) + return false; + S32 a_sort = aitem->getSortField(); + S32 b_sort = bitem->getSortField(); + return a_sort < b_sort; + } + }*/ + // We sort by name if we aren't sorting by date // OR if these are folders and we are sorting folders by name. bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - + || (mFoldersByName + && (a->getSortGroup() != SG_ITEM))); + if (a->getSortGroup() != b->getSortGroup()) { if (mSystemToTop) diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index ae19b4082..ca9da12ea 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -60,10 +60,10 @@ class LLInventorySort public: LLInventorySort() : mSortOrder(0), - mByDate(false), - mSystemToTop(false), - mFoldersByName(false) { } - + mByDate(false), + mSystemToTop(false), + mFoldersByName(false) { } + // Returns true if order has changed bool updateSort(U32 order); U32 getSort() { return mSortOrder; } @@ -92,7 +92,7 @@ public: friend class LLFolderViewEventListener; static const S32 LEFT_PAD = 5; - static const S32 LEFT_INDENTATION = 13; + static const S32 LEFT_INDENTATION = 6; static const S32 ICON_PAD = 2; static const S32 ICON_WIDTH = 16; static const S32 TEXT_PAD = 1; @@ -101,19 +101,13 @@ public: // animation parameters static const F32 FOLDER_CLOSE_TIME_CONSTANT; static const F32 FOLDER_OPEN_TIME_CONSTANT; + + BOOL isLoading() const { return mIsLoading; } + private: BOOL mIsSelected; protected: - static const LLFontGL* sFont; - static const LLFontGL* sSmallFont; - static LLColor4 sFgColor; - static LLColor4 sHighlightBgColor; - static LLColor4 sHighlightFgColor; - static LLColor4 sFilterBGColor; - static LLColor4 sFilterTextColor; - static LLColor4 sSuffixColor; - static LLColor4 sSearchStatusColor; static LLUIImagePtr sArrowImage; static LLUIImagePtr sBoxImage; @@ -121,10 +115,9 @@ protected: std::string mSearchableLabel; std::string mSearchableLabelDesc; std::string mSearchableLabelCreator; - std::string mSearchable; - std::string mType; S32 mLabelWidth; - U32 mCreationDate; + bool mLabelWidthDirty; + time_t mCreationDate; LLFolderViewFolder* mParentFolder; LLFolderViewEventListener* mListener; BOOL mIsCurSelection; @@ -133,8 +126,11 @@ protected: std::string mLabelSuffix; LLUIImagePtr mIcon; std::string mStatusText; + LLUIImagePtr mIconOpen; + LLUIImagePtr mIconOverlay; BOOL mHasVisibleChildren; S32 mIndentation; + S32 mItemHeight; BOOL mPassedFilter; S32 mLastFilterGeneration; std::string::size_type mStringMatchOffset; @@ -143,19 +139,31 @@ protected: BOOL mDragAndDropTarget; BOOL mIsLoading; LLTimer mTimeSinceRequestStart; + bool mShowLoadStatus; + + std::string mSearchable; + U32 mSearchType; // helper function to change the selection from the root. void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); - // helper function to change the selection from the root. - void extendSelectionFromRoot(LLFolderViewItem* selection); + //Sets extra search criteria 'labels' to be compared against by filter. + void updateExtraSearchCriteria(); + //Update mSearchable string based on roots search flags. (Name, creator, desc) + void updateSearchLabelType(); // this is an internal method used for adding items to folders. A - // no-op at this leve, but reimplemented in derived classes. + // no-op at this level, but reimplemented in derived classes. virtual BOOL addItem(LLFolderViewItem*) { return FALSE; } virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; } + static LLFontGL* getLabelFontForStyle(U8 style); + + virtual void setCreationDate(time_t creation_date_utc) { mCreationDate = creation_date_utc; } + public: + BOOL postBuild(); + // This function clears the currently selected item, and records // the specified selected item appropriately for display and use // in the UI. If open is TRUE, then folders are opened up along @@ -163,16 +171,16 @@ public: void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus = TRUE); - - // This function is called when the folder view is dirty. It's // implemented here but called by derived classes when folding the // views. void arrangeFromRoot(); void filterFromRoot( void ); + + void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus); // creation_date is in UTC seconds - LLFolderViewItem( const std::string& name, LLUIImagePtr icon, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener ); + LLFolderViewItem( const std::string& name, LLUIImagePtr icon, LLUIImagePtr icon_open, LLUIImagePtr icon_overlay, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener ); virtual ~LLFolderViewItem( void ); // addToFolder() returns TRUE if it succeeds. FALSE otherwise @@ -203,9 +211,6 @@ public: // Returns TRUE if the selection state of this item was changed. virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - // this method is used to group select items - virtual void extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items) { } - // this method is used to deselect this element void deselectItem(); @@ -213,7 +218,7 @@ public: virtual void selectItem(); // gets multiple-element selection - virtual BOOL getSelectionList(std::set &selection){return TRUE;} + virtual std::set getSelectionList() const; // Returns true is this object and all of its children can be removed (deleted by user) virtual BOOL isRemovable(); @@ -226,12 +231,16 @@ public: BOOL isSelected() const { return mIsSelected; } + void setUnselected() { mIsSelected = FALSE; } + void setIsCurSelection(BOOL select) { mIsCurSelection = select; } BOOL getIsCurSelection() { return mIsCurSelection; } BOOL hasVisibleChildren() { return mHasVisibleChildren; } + void setShowLoadStatus(bool status) { mShowLoadStatus = status; } + // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. //virtual BOOL removeRecursively(BOOL single_item); @@ -244,7 +253,7 @@ public: // viewed. This method will ask the viewed object itself. const std::string& getName( void ) const; - std::string& getSearchableLabel( void ); + const std::string& getSearchableLabel( void ); // This method returns the label displayed on the view. This // method was primarily added to allow sorting on the folder @@ -253,10 +262,10 @@ public: // Used for sorting, like getLabel() above. virtual time_t getCreationDate() const { return mCreationDate; } - + LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } - + LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE ); LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE ); @@ -276,7 +285,7 @@ public: // Show children (unfortunate that this is called "open") virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() { return FALSE; } + virtual BOOL isOpen() const { return FALSE; } virtual LLFolderView* getRoot(); BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); @@ -305,13 +314,24 @@ public: virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual LLView* getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const + { + if(create_if_missing) + return LLView::getChildView(name, recurse, TRUE); + else + return NULL; + } + // virtual void handleDropped(); virtual void draw(); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + +private: + static std::map sFonts; // map of styles to fonts }; @@ -331,11 +351,13 @@ class LLFolderViewFolder : public LLFolderViewItem { protected: LLFolderViewFolder( const std::string& name, LLUIImagePtr icon, + LLUIImagePtr icon_open, + LLUIImagePtr icon_link, LLFolderView* root, LLFolderViewEventListener* listener ); friend class LLBuildNewViewsScheduler; - friend class LLPanelInventory; + friend class LLPanelObjectInventory; friend class LLInventoryPanel; public: @@ -347,13 +369,6 @@ public: typedef std::list items_t; typedef std::list folders_t; -private: - S32 mNumDescendantsSelected; - -public: // Accessed needed by LLFolderViewItem - void recursiveIncrementNumDescendantsSelected(S32 increment); - S32 numSelected(void) const { return mNumDescendantsSelected + (isSelected() ? 1 : 0); } - protected: items_t mItems; folders_t mFolders; @@ -370,6 +385,9 @@ protected: S32 mLastCalculatedWidth; S32 mCompletedFilterGeneration; S32 mMostFilteredDescendantGeneration; + bool mNeedsSort; + bool mPassedFolderFilter; + public: typedef enum e_recurse_type { @@ -395,6 +413,7 @@ public: virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); BOOL needsArrange(); + void requestSort(); // Returns the sort group (system, trash, folder) for this folder. virtual EInventorySortGroup getSortGroup() const; @@ -402,13 +421,21 @@ public: virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up); virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; } - BOOL hasFilteredDescendants(S32 filter_generation) { return mMostFilteredDescendantGeneration >= filter_generation; } + BOOL hasFilteredDescendants(S32 filter_generation); BOOL hasFilteredDescendants(); // applies filters to control visibility of inventory items virtual void filter( LLInventoryFilter& filter); virtual void setFiltered(BOOL filtered, S32 filter_generation); + virtual BOOL getFiltered(); + virtual BOOL getFiltered(S32 filter_generation); + virtual void dirtyFilter(); + + // folder-specific filtering (filter status propagates top down instead of bottom up) + void filterFolder(LLInventoryFilter& filter); + void setFilteredFolder(bool filtered, S32 filter_generation); + bool getFilteredFolder(S32 filter_generation); // Passes selection information on to children and record // selection information if necessary. @@ -423,10 +450,7 @@ public: virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); // this method is used to group select items - virtual void extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items); - - // Deselect this folder and all folder/items it contains recursively. - void recursiveDeselect(BOOL deselect_self); + void extendSelectionTo(LLFolderViewItem* selection); // Returns true is this object and all of its children can be removed. virtual BOOL isRemovable(); @@ -482,21 +506,24 @@ public: virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO); // Get the current state of the folder. - virtual BOOL isOpen() { return mIsOpen; } + virtual BOOL isOpen() const { return mIsOpen; } // special case if an object is dropped on the child. BOOL handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); + BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); void applyFunctorRecursively(LLFolderViewFunctor& functor); virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); + // Just apply this functor to the folder's immediate children. + void applyFunctorToChildren(LLFolderViewFunctor& functor); + virtual void openItem( void ); - virtual BOOL addItem(LLFolderViewItem* item); + virtual BOOL addItem(LLFolderViewItem* item); virtual BOOL addFolder( LLFolderViewFolder* folder); // LLView functionality @@ -505,15 +532,19 @@ public: virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + BOOL handleDragAndDropToThisFolder(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); virtual void draw(); time_t getCreationDate() const; bool isTrash() const; - S32 getNumSelectedDescendants(void) const { return mNumDescendantsSelected; } folders_t::const_iterator getFoldersBegin() const { return mFolders.begin(); } folders_t::const_iterator getFoldersEnd() const { return mFolders.end(); } @@ -522,6 +553,8 @@ public: items_t::const_iterator getItemsBegin() const { return mItems.begin(); } items_t::const_iterator getItemsEnd() const { return mItems.end(); } items_t::size_type getItemsCount() const { return mItems.size(); } + LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse); + void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector& items); }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index f136c144e..9fe20b6b0 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -2,31 +2,25 @@ * @file llgesturemgr.cpp * @brief Manager for playing gestures on the viewer * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,11 +31,12 @@ // system #include #include -#include // library +#include "llaudioengine.h" #include "lldatapacker.h" #include "llinventory.h" +#include "llkeyframemotion.h" #include "llmultigesture.h" #include "llnotificationsutil.h" #include "llstl.h" @@ -53,10 +48,11 @@ #include "llagent.h" #include "llchatbar.h" #include "lldelayedgestureerror.h" -#include "llinventoryobserver.h" +#include "llinventorymodel.h" #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" +#include "llappearancemgr.h" #include "chatbar_as_cmdline.h" @@ -78,7 +74,9 @@ LLGestureMgr::LLGestureMgr() mPlaying(), mActive(), mLoadingCount(0) -{ } +{ + gInventory.addObserver(this); +} // We own the data for gestures, so clean them up. @@ -92,6 +90,7 @@ LLGestureMgr::~LLGestureMgr() delete gesture; gesture = NULL; } + gInventory.removeObserver(this); } @@ -100,6 +99,41 @@ void LLGestureMgr::init() // TODO } +void LLGestureMgr::changed(U32 mask) +{ + LLInventoryFetchItemsObserver::changed(mask); + + if (mask & LLInventoryObserver::GESTURE) + { + // If there was a gesture label changed, update all the names in the + // active gestures and then notify observers + if (mask & LLInventoryObserver::LABEL) + { + for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it) + { + if(it->second) + { + LLViewerInventoryItem* item = gInventory.getItem(it->first); + if(item) + { + it->second->mName = item->getName(); + } + } + } + notifyObservers(); + } + // If there was a gesture added or removed notify observers + // STRUCTURE denotes that the inventory item has been moved + // In the case of deleting gesture, it is moved to the trash + else if(mask & LLInventoryObserver::ADD || + mask & LLInventoryObserver::REMOVE || + mask & LLInventoryObserver::STRUCTURE) + { + notifyObservers(); + } + } +} + // Use this version when you have the item_id but not the asset_id, // and you KNOW the inventory is loaded. @@ -307,6 +341,8 @@ void LLGestureMgr::deactivateGesture(const LLUUID& item_id) gAgent.sendReliableMessage(); + LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id, false); + notifyObservers(); } @@ -496,6 +532,66 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture) gesture->mPlaying = TRUE; mPlaying.push_back(gesture); + // Load all needed assets to minimize the delays + // when gesture is playing. + for (std::vector::iterator steps_it = gesture->mSteps.begin(); + steps_it != gesture->mSteps.end(); + ++steps_it) + { + LLGestureStep* step = *steps_it; + switch(step->getType()) + { + case STEP_ANIMATION: + { + LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; + const LLUUID& anim_id = anim_step->mAnimAssetID; + + // Don't request the animation if this step stops it or if it is already in Static VFS + if (!(anim_id.isNull() + || anim_step->mFlags & ANIM_FLAG_STOP + || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION))) + { + mLoadingAssets.insert(anim_id); + + LLUUID* id = new LLUUID(gAgentID); + gAssetStorage->getAssetData(anim_id, + LLAssetType::AT_ANIMATION, + onAssetLoadComplete, + (void *)id, + TRUE); + } + break; + } + case STEP_SOUND: + { + LLGestureStepSound* sound_step = (LLGestureStepSound*)step; + const LLUUID& sound_id = sound_step->mSoundAssetID; + if (!(sound_id.isNull() + || gAssetStorage->hasLocalAsset(sound_id, LLAssetType::AT_SOUND))) + { + mLoadingAssets.insert(sound_id); + + gAssetStorage->getAssetData(sound_id, + LLAssetType::AT_SOUND, + onAssetLoadComplete, + NULL, + TRUE); + } + break; + } + case STEP_CHAT: + case STEP_WAIT: + case STEP_EOF: + { + break; + } + default: + { + llwarns << "Unknown gesture step type: " << step->getType() << llendl; + } + } + } + // And get it going stepGesture(gesture); @@ -711,8 +807,7 @@ void LLGestureMgr::stepGesture(LLMultiGesture* gesture) { return; } - LLVOAvatar* avatar = gAgentAvatarp; - if (!avatar) return; + if (!isAgentAvatarValid() || hasLoadingAssets(gesture)) return; // Of the ones that started playing, have any stopped? @@ -723,8 +818,8 @@ void LLGestureMgr::stepGesture(LLMultiGesture* gesture) { // look in signaled animations (simulator's view of what is // currently playing. - LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it); - if (play_it != avatar->mSignaledAnimations.end()) + LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it); + if (play_it != gAgentAvatarp->mSignaledAnimations.end()) { ++gest_it; } @@ -742,8 +837,8 @@ void LLGestureMgr::stepGesture(LLMultiGesture* gesture) gest_it != gesture->mRequestedAnimIDs.end(); ) { - LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it); - if (play_it != avatar->mSignaledAnimations.end()) + LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it); + if (play_it != gAgentAvatarp->mSignaledAnimations.end()) { // Hooray, this animation has started playing! // Copy into playing. @@ -998,10 +1093,22 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, } } + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if(item) + { + gesture->mName = item->getName(); + } + else + { + // Watch this item and set gesture name when item exists in inventory + self.setFetchID(item_id); + self.startFetch(); + } self.mActive[item_id] = gesture; // Everything has been successful. Add to the active list. gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + if (inform_server) { // Inform the database of this change @@ -1019,6 +1126,13 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, gAgent.sendReliableMessage(); } + callback_map_t::iterator i_cb = self.mCallbackMap.find(item_id); + + if(i_cb != self.mCallbackMap.end()) + { + i_cb->second(gesture); + self.mCallbackMap.erase(i_cb); + } self.notifyObservers(); } @@ -1055,7 +1169,98 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, } } +// static +void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, + const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) +{ + LLGestureMgr& self = LLGestureMgr::instance(); + // Complete the asset loading process depending on the type and + // remove the asset id from pending downloads list. + switch(type) + { + case LLAssetType::AT_ANIMATION: + { + LLKeyframeMotion::onLoadComplete(vfs, asset_uuid, type, user_data, status, ext_status); + + self.mLoadingAssets.erase(asset_uuid); + + break; + } + case LLAssetType::AT_SOUND: + { + LLAudioEngine::assetCallback(vfs, asset_uuid, type, user_data, status, ext_status); + + self.mLoadingAssets.erase(asset_uuid); + + break; + } + default: + { + llwarns << "Unexpected asset type: " << type << llendl; + + // We don't want to return from this callback without + // an animation or sound callback being fired + // and *user_data handled to avoid memory leaks. + llassert(type == LLAssetType::AT_ANIMATION || type == LLAssetType::AT_SOUND); + } + } +} + +// static +bool LLGestureMgr::hasLoadingAssets(LLMultiGesture* gesture) +{ + LLGestureMgr& self = LLGestureMgr::instance(); + + for (std::vector::iterator steps_it = gesture->mSteps.begin(); + steps_it != gesture->mSteps.end(); + ++steps_it) + { + LLGestureStep* step = *steps_it; + switch(step->getType()) + { + case STEP_ANIMATION: + { + LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; + const LLUUID& anim_id = anim_step->mAnimAssetID; + + if (!(anim_id.isNull() + || anim_step->mFlags & ANIM_FLAG_STOP + || self.mLoadingAssets.find(anim_id) == self.mLoadingAssets.end())) + { + return true; + } + break; + } + case STEP_SOUND: + { + LLGestureStepSound* sound_step = (LLGestureStepSound*)step; + const LLUUID& sound_id = sound_step->mSoundAssetID; + + if (!(sound_id.isNull() + || self.mLoadingAssets.find(sound_id) == self.mLoadingAssets.end())) + { + return true; + } + break; + } + case STEP_CHAT: + case STEP_WAIT: + case STEP_EOF: + { + break; + } + default: + { + llwarns << "Unknown gesture step type: " << step->getType() << llendl; + } + } + } + + return false; +} void LLGestureMgr::stopGesture(LLMultiGesture* gesture) { @@ -1181,6 +1386,27 @@ void LLGestureMgr::getItemIDs(uuid_vec_t* ids) } } +void LLGestureMgr::done() +{ + bool notify = false; + for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it) + { + if(it->second && it->second->mName.empty()) + { + LLViewerInventoryItem* item = gInventory.getItem(it->first); + if(item) + { + it->second->mName = item->getName(); + notify = true; + } + } + } + if(notify) + { + notifyObservers(); + } +} + // static const LLUUID& get_linked_uuid(const LLUUID &item_id) { diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 88d3b9c4a..8211496f9 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -2,31 +2,25 @@ * @file llgesturemgr.h * @brief Manager for playing gestures on the viewer * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,6 +32,8 @@ #include #include "llassetstorage.h" // LLAssetType +#include "llinventoryobserver.h" +#include "llsingleton.h" #include "llviewerinventory.h" class LLMultiGesture; @@ -52,11 +48,14 @@ public: virtual void changed() = 0; }; -class LLGestureMgr : public LLSingleton +class LLGestureMgr : public LLSingleton, public LLInventoryFetchItemsObserver { public: + + typedef boost::function gesture_loaded_callback_t; // Maps inventory item_id to gesture typedef std::map item_map_t; + typedef std::map callback_map_t; LLGestureMgr(); ~LLGestureMgr(); @@ -110,7 +109,15 @@ public: // Also remove from playing list void stopGesture(LLMultiGesture* gesture); void stopGesture(const LLUUID& item_id); - + /** + * Add cb into callbackMap. + * Note: + * Manager will call cb after gesture will be loaded and will remove cb automatically. + */ + void setGestureLoadedCallback(LLUUID inv_item_id, gesture_loaded_callback_t cb) + { + mCallbackMap[inv_item_id] = cb; + } // Trigger the first gesture that matches this key. // Returns TRUE if it finds a gesture bound to that key. BOOL triggerGesture(KEY key, MASK mask); @@ -127,6 +134,9 @@ public: void removeObserver(LLGestureManagerObserver* observer); void notifyObservers(); + // Overriding so we can update active gesture names and notify observers + void changed(U32 mask); + BOOL matchPrefix(const std::string& in_str, std::string* out_str); // Copy item ids into the vector @@ -139,14 +149,25 @@ protected: // Do a single step in a gesture void runStep(LLMultiGesture* gesture, LLGestureStep* step); + // LLInventoryCompletionObserver trigger + void done(); + // Used by loadGesture static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); + // Used by playGesture to load an asset file + // required to play a gesture step + static void onAssetLoadComplete(LLVFS *vfs, + const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); - + // Checks whether all animation and sound assets + // needed to play a gesture are loaded. + static bool hasLoadingAssets(LLMultiGesture* gesture); private: // Active gestures. @@ -157,9 +178,13 @@ private: S32 mLoadingCount; std::string mDeactivateSimilarNames; - + std::vector mObservers; + callback_map_t mCallbackMap; std::vector mPlaying; BOOL mValid; + + std::set mLoadingAssets; }; + #endif diff --git a/indra/newview/llgroupnotify.cpp b/indra/newview/llgroupnotify.cpp index 3eabf6df1..24ff0408e 100644 --- a/indra/newview/llgroupnotify.cpp +++ b/indra/newview/llgroupnotify.cpp @@ -417,7 +417,7 @@ void LLGroupNotifyBox::moveToBack() { // Move this dialog to the back. gNotifyBoxView->removeChild(this); - gNotifyBoxView->addChildAtEnd(this); + gNotifyBoxView->addChildInBack(this); mNextBtn->setVisible(FALSE); // And enable the next button on the frontmost one, if there is one diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index e1c3ab443..842f10fd6 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1344,10 +1344,7 @@ LLFloaterIMPanel::~LLFloaterIMPanel() mVoiceChannel = NULL; //delete focus lost callback - if(mInputEditor) - { - mInputEditor->setFocusLostCallback( NULL ); - } + mFocusLostSignal.disconnect(); } BOOL LLFloaterIMPanel::postBuild() @@ -1360,8 +1357,8 @@ BOOL LLFloaterIMPanel::postBuild() mRPMode = false; mInputEditor = getChild("chat_editor"); - mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); - mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); + mInputEditor->setFocusReceivedCallback( boost::bind(&LLFloaterIMPanel::onInputEditorFocusReceived, this) ); + mFocusLostSignal = mInputEditor->setFocusLostCallback( boost::bind(&LLFloaterIMPanel::onInputEditorFocusLost, this) ); mInputEditor->setKeystrokeCallback( onInputEditorKeystroke ); mInputEditor->setCommitCallback( onCommitChat ); mInputEditor->setCallbackUserData(this); @@ -2005,18 +2002,14 @@ void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata) self->sendMsg(); } -// static -void LLFloaterIMPanel::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) +void LLFloaterIMPanel::onInputEditorFocusReceived() { - LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; - self->mHistoryEditor->setCursorAndScrollToEnd(); + mHistoryEditor->setCursorAndScrollToEnd(); } -// static -void LLFloaterIMPanel::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) +void LLFloaterIMPanel::onInputEditorFocusLost() { - LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; - self->setTyping(FALSE); + setTyping(FALSE); } // static diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 69cd8f19e..9daed909f 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -231,8 +231,8 @@ public: void *cargo_data, EAcceptance *accept, std::string& tooltip_msg); - static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + void onInputEditorFocusReceived(); + void onInputEditorFocusLost(); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); static void onCommitChat(LLUICtrl* caller, void* userdata); static void onTabClick( void* userdata ); @@ -382,6 +382,10 @@ private: // Timer to detect when user has stopped typing. LLFrameTimer mLastKeystrokeTimer; + boost::signals2::connection mFocusLostSignal; + + + void disableWhileSessionStarting(); typedef std::map styleMap; diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index eba565ca9..0cb51a4bb 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -35,12 +35,13 @@ #include // for std::pair<> #include "llinventorypanel.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llinventorybridge.h" #include "message.h" #include "llagent.h" +#include "llagentwearables.h" #include "llcallingcard.h" #include "llcheckboxctrl.h" // for radio buttons #include "llfoldervieweventlistener.h" @@ -105,7 +106,7 @@ const std::string NEW_LSL_NAME = "New Script"; // *TODO:Translate? (probably not const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably not) const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not) -typedef LLMemberListener object_inventory_listener_t; +typedef LLMemberListener object_inventory_listener_t; typedef LLMemberListener inventory_listener_t; typedef LLMemberListener inventory_panel_listener_t; @@ -128,8 +129,7 @@ bool doToSelected(LLFolderView* folder, std::string action) LLInventoryClipboard::instance().reset(); } - std::set selected_items; - folder->getSelectionList(selected_items); + std::set selected_items = folder->getSelectionList(); LLMultiPreview* multi_previewp = NULL; LLMultiProperties* multi_propertiesp = NULL; @@ -165,15 +165,9 @@ bool doToSelected(LLFolderView* folder, std::string action) LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); if(!bridge) continue; - bridge->performAction(folder, model, action); + bridge->performAction(model, action); } - - - - - - LLFloater::setFloaterHost(NULL); if (multi_previewp) { @@ -192,7 +186,7 @@ class LLDoToSelectedPanel : public object_inventory_listener_t bool handleEvent(LLPointer event, const LLSD& userdata) { std::string action = userdata.asString(); - LLPanelInventory *panel = mPtr; + LLPanelObjectInventory *panel = mPtr; LLFolderView* folder = panel->getRootFolder(); if(!folder) return true; @@ -237,7 +231,7 @@ class LLNewWindow : public inventory_listener_t LLInventoryView* iv = new LLInventoryView(std::string("Inventory"), rect, mPtr->getActivePanel()->getModel()); - iv->getActivePanel()->setFilterTypes(mPtr->getActivePanel()->getFilterTypes()); + iv->getActivePanel()->setFilterTypes(mPtr->getActivePanel()->getFilterObjectTypes()); iv->getActivePanel()->setFilterSubString(mPtr->getActivePanel()->getFilterSubString()); iv->open(); /*Flawfinder: ignore*/ @@ -391,12 +385,7 @@ void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type, else { LLWearableType::EType wear_type = LLWearableType::typeNameToType(type); - if(wear_type != LLWearableType::WT_NONE) - { - LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(LLWearableType::getAssetType(wear_type)); - LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(folder_type); - LLFolderBridge::createWearable(parent_id, wear_type); - } + LLAgentWearables::createWearable(wear_type, false, self ? self->getUUID() : LLUUID::null); } ptr->getRootFolder()->setNeedsAutoRename(TRUE); } @@ -547,8 +536,7 @@ class LLBeginIMSession : public inventory_panel_listener_t LLInventoryPanel *panel = mPtr; LLInventoryModel* model = panel->getModel(); if(!model) return true; - std::set selected_items; - panel->getRootFolder()->getSelectionList(selected_items); + std::set selected_items = panel->getRootFolder()->getSelectionList(); std::string name; static int session_num = 1; @@ -653,8 +641,7 @@ class LLAttachObject : public inventory_panel_listener_t LLFolderView* folder = panel->getRootFolder(); if(!folder) return true; - std::set selected_items; - folder->getSelectionList(selected_items); + std::set selected_items = folder->getSelectionList(); LLUUID id = *selected_items.begin(); std::string joint_name = userdata.asString(); @@ -709,7 +696,7 @@ class LL : public listener_t }; */ -void init_object_inventory_panel_actions(LLPanelInventory *panel) +void init_object_inventory_panel_actions(LLPanelObjectInventory *panel) { (new LLDoToSelectedPanel())->registerListener(panel, "Inventory.DoToSelected"); } diff --git a/indra/newview/llinventorybackup.cpp b/indra/newview/llinventorybackup.cpp index 45a1b68bf..b3caf401f 100644 --- a/indra/newview/llinventorybackup.cpp +++ b/indra/newview/llinventorybackup.cpp @@ -489,8 +489,7 @@ void LLInventoryBackup::save(LLFolderView* folder) { LLInventoryModel* model = &gInventory; - std::set selected_items; - folder->getSelectionList(selected_items); + std::set selected_items = folder->getSelectionList(); if(selected_items.size() < 1) { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index dab9d42c9..2bc5fcfaa 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -41,11 +41,13 @@ #include "llinventoryicon.h" #include "llinventorymodelbackgroundfetch.h" +#include "lltrans.h" + #include "message.h" -#include "cofmgr.h" #include "llagent.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "llagentcamera.h" #include "llcallingcard.h" #include "llcheckboxctrl.h" // for radio buttons @@ -128,6 +130,11 @@ #include "llattachmentsmgr.h" // [/RLVa:KB] +bool InventoryLinksEnabled() +{ + return gHippoGridManager->getConnectedGrid()->supportsInvLinks(); +} + typedef std::pair two_uuids_t; typedef std::list two_uuids_list_t; @@ -158,15 +165,23 @@ void dec_busy_count() } // Function declarations -struct LLWearableHoldingPattern; -void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append, BOOL replace = FALSE); -void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata); -void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*); -void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append); void remove_inventory_category_from_avatar(LLInventoryCategory* category); -void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata); +void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id); bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); -bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response); +bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); +static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); + +// Helper functions + +bool isAddAction(const std::string& action) +{ + return ("wear" == action || "attach" == action || "activate" == action); +} + +bool isRemoveAction(const std::string& action) +{ + return ("take_off" == action || "detach" == action || "deactivate" == action); +} // void gotImageForSaveItemAs(BOOL success, @@ -182,12 +197,6 @@ void gotAssetForSaveItemAs(LLVFS *vfs, void* user_data, S32 status, LLExtStat ext_status); // -struct LLWearInfo -{ - LLUUID mCategoryID; - BOOL mAppend; - BOOL mReplace; -}; // [RLVa:KB] - Made this part of LLWearableHoldingPattern //BOOL gAddToOutfit = FALSE; @@ -198,14 +207,14 @@ struct LLWearInfo // +=================================================+ LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : mUUID(uuid), - //mRoot(root), + mRoot(root), mInvType(LLInventoryType::IT_NONE), mIsLink(FALSE) { - mInventoryPanel = inventory; + mInventoryPanel = inventory->getHandle(); const LLInventoryObject* obj = getInventoryObject(); mIsLink = obj && obj->getIsLinkType(); } @@ -228,7 +237,6 @@ const std::string& LLInvFVBridge::getDisplayName() const // Folders have full perms PermissionMask LLInvFVBridge::getPermissionMask() const { - return PERM_ALL; } @@ -246,13 +254,13 @@ time_t LLInvFVBridge::getCreationDate() const } // Can be destroyed (or moved to trash) -BOOL LLInvFVBridge::isItemRemovable() +BOOL LLInvFVBridge::isItemRemovable() const { return get_is_item_removable(getInventoryModel(), mUUID); } // Can be moved to another folder -BOOL LLInvFVBridge::isItemMovable() +BOOL LLInvFVBridge::isItemMovable() const { return TRUE; } @@ -262,10 +270,27 @@ BOOL LLInvFVBridge::isLink() const return mIsLink; } +/*virtual*/ +/** + * @brief Adds this item into clipboard storage + */ +//Unused. +void LLInvFVBridge::cutToClipboard() +{ + if(isItemMovable()) + { + LLInventoryClipboard::instance().cut(mUUID); + } +} // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { - LLShowProps::showProperties(mUUID); + show_item_profile(mUUID); + + // Disable old properties floater; this is replaced by the sidepanel. + /* + LLFloaterReg::showInstance("properties", mUUID); + */ } void LLInvFVBridge::removeBatch(LLDynamicArray& batch) @@ -408,7 +433,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArraygetParentUUID()]; ++update[trash_id]; // - if(!gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot)) + if(!gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot)) //Avoid fake items. { // if(start_new_message) @@ -526,7 +551,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const BOOL LLInvFVBridge::isClipboardPasteableAsLink() const { - if (!gHippoGridManager->getConnectedGrid()->supportsInvLinks()) + if (!InventoryLinksEnabled()) { return FALSE; } @@ -568,6 +593,10 @@ void hide_context_entries(LLMenuGL& menu, { const LLView::child_list_t *list = menu.getChildList(); + // For removing double separators or leading separator. Start at true so that + // if the first element is a separator, it will not be shown. + bool is_previous_entry_separator = true; + for (LLView::child_list_t::const_iterator itor = list->begin(); itor != list->end(); ++itor) @@ -581,8 +610,7 @@ void hide_context_entries(LLMenuGL& menu, { hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); } - - + bool found = false; menuentry_vec_t::const_iterator itor2; for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2) @@ -590,21 +618,41 @@ void hide_context_entries(LLMenuGL& menu, if (*itor2 == name) { found = true; + break; } } + + // Don't allow multiple separators in a row (e.g. such as if there are no items + // between two separators). + if (found) + { + const bool is_entry_separator = (dynamic_cast(menu_item) != NULL); + found = !(is_entry_separator && is_previous_entry_separator); + is_previous_entry_separator = is_entry_separator; + } + if (!found) { - menu_item->setVisible(FALSE); + if (!menu_item->getLastVisible()) + { + menu_item->setVisible(FALSE); + } + + menu_item->setEnabled(FALSE); } else { - for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) + menu_item->setVisible(TRUE); + // A bit of a hack so we can remember that some UI element explicitly set this to be visible + // so that some other UI element from multi-select doesn't later set this invisible. + menu_item->pushVisible(TRUE); + + bool enabled = (menu_item->getEnabled() == TRUE); + for (itor2 = disabled_entries.begin(); enabled && (itor2 != disabled_entries.end()); ++itor2) { - if (*itor2 == name) - { - menu_item->setEnabled(FALSE); - } + enabled &= (*itor2 != name); } + menu_item->setEnabled(enabled); } } } @@ -676,10 +724,13 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Paste")); } - items.push_back(std::string("Paste As Link")); - if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) + if(InventoryLinksEnabled()) { - disabled_items.push_back(std::string("Paste As Link")); + items.push_back(std::string("Paste As Link")); + if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Paste As Link")); + } } items.push_back(std::string("Paste Separator")); @@ -711,7 +762,8 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // [RLVa:KB] - Checked: 2009-11-11 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a if (rlv_handler_t::isEnabled()) { - LLInventoryObject* pItem = (mInventoryPanel->getModel()) ? mInventoryPanel->getModel()->getObject(mUUID) : NULL; + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + LLInventoryObject* pItem = (panel && panel->getModel()) ? panel->getModel()->getObject(mUUID) : NULL; if ( (pItem) && ( ((LLAssetType::AT_NOTECARD == pItem->getType()) && (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWNOTE))) || ((LLAssetType::AT_LSL_TEXT == pItem->getType()) && (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWSCRIPT))) || @@ -760,7 +812,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, } // "Remove link" and "Delete" are the same operation. - if (obj && obj->getIsLinkType() /* && !get_is_item_worn(mUUID)*/) + if (obj && obj->getIsLinkType() && !get_is_item_worn(mUUID)) { items.push_back(std::string("Remove Link")); } @@ -828,7 +880,7 @@ LLInventoryObject* LLInvFVBridge::getInventoryObject() const LLInventoryModel* LLInvFVBridge::getInventoryModel() const { - LLInventoryPanel* panel = dynamic_cast(mInventoryPanel); + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); return panel ? panel->getModel() : NULL; } @@ -879,7 +931,7 @@ BOOL LLInvFVBridge::isAgentInventory() const BOOL LLInvFVBridge::isCOFFolder() const { - return LLCOFMgr::instance().getCOF() == mUUID; + return LLAppearanceMgr::instance().getIsInCOF(mUUID); } BOOL LLInvFVBridge::isItemPermissive() const @@ -906,132 +958,137 @@ void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, } LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, - LLAssetType::EType actual_asset_type, - LLInventoryType::EType inv_type, - LLInventoryPanel* inventory, - const LLUUID& uuid, - U32 flags) + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags) { LLInvFVBridge* new_listener = NULL; switch(asset_type) { - case LLAssetType::AT_TEXTURE: - if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) - { + case LLAssetType::AT_TEXTURE: + if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) + { llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLTextureBridge(inventory, uuid, inv_type); - break; + } + new_listener = new LLTextureBridge(inventory, root, uuid, inv_type); + break; - case LLAssetType::AT_SOUND: - if(!(inv_type == LLInventoryType::IT_SOUND)) - { + case LLAssetType::AT_SOUND: + if(!(inv_type == LLInventoryType::IT_SOUND)) + { llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLSoundBridge(inventory, uuid); - break; + } + new_listener = new LLSoundBridge(inventory, root, uuid); + break; - case LLAssetType::AT_LANDMARK: - if(!(inv_type == LLInventoryType::IT_LANDMARK)) - { + case LLAssetType::AT_LANDMARK: + if(!(inv_type == LLInventoryType::IT_LANDMARK)) + { llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLandmarkBridge(inventory, uuid, flags); - break; - - case LLAssetType::AT_CALLINGCARD: - if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLCallingCardBridge(inventory, uuid); - break; + } + new_listener = new LLLandmarkBridge(inventory, root, uuid, flags); + break; - case LLAssetType::AT_SCRIPT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { + case LLAssetType::AT_CALLINGCARD: + if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) + { llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLItemBridge(inventory, uuid); - break; + } + new_listener = new LLCallingCardBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_SCRIPT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLItemBridge(inventory, root, uuid); + break; case LLAssetType::AT_OBJECT: if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT) || inv_type == LLInventoryType::IT_TEXTURE ) //There's an abundance of objects in inv that have texture (0) as their inv type, right out of unpack. //May have been bug either in an old client, or server version. Either way... it causes a lot of spam over something ultimately harmless. - { + { llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags); - break; - - case LLAssetType::AT_NOTECARD: - if(!(inv_type == LLInventoryType::IT_NOTECARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLNotecardBridge(inventory, uuid); - break; - - case LLAssetType::AT_ANIMATION: - if(!(inv_type == LLInventoryType::IT_ANIMATION)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLAnimationBridge(inventory, uuid); - break; - - case LLAssetType::AT_GESTURE: - if(!(inv_type == LLInventoryType::IT_GESTURE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLGestureBridge(inventory, uuid); - break; - - case LLAssetType::AT_LSL_TEXT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLSLTextBridge(inventory, uuid); - break; - - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - if(!(inv_type == LLInventoryType::IT_WEARABLE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (LLWearableType::EType)flags); - break; - - case LLAssetType::AT_CATEGORY: - //case LLAssetType::AT_ROOT_CATEGORY: - if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) - { - // Create a link folder handler instead. - new_listener = new LLLinkFolderBridge(inventory, uuid); + } + new_listener = new LLObjectBridge(inventory, root, uuid, inv_type, flags); + break; + + case LLAssetType::AT_NOTECARD: + if(!(inv_type == LLInventoryType::IT_NOTECARD)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLNotecardBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_ANIMATION: + if(!(inv_type == LLInventoryType::IT_ANIMATION)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLAnimationBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_GESTURE: + if(!(inv_type == LLInventoryType::IT_GESTURE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLGestureBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_LSL_TEXT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLLSLTextBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + if(!(inv_type == LLInventoryType::IT_WEARABLE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, (LLWearableType::EType)flags); + break; + case LLAssetType::AT_CATEGORY: + if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + { + // Create a link folder handler instead. + new_listener = new LLLinkFolderBridge(inventory, root, uuid); + break; + } + new_listener = new LLFolderBridge(inventory, root, uuid); + break; + case LLAssetType::AT_LINK: + case LLAssetType::AT_LINK_FOLDER: + // Only should happen for broken links. + new_listener = new LLLinkItemBridge(inventory, root, uuid); + break; + case LLAssetType::AT_MESH: + if(!(inv_type == LLInventoryType::IT_MESH)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLMeshBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_IMAGE_TGA: + case LLAssetType::AT_IMAGE_JPEG: + //llwarns << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << llendl; + break; + + default: + llinfos << "Unhandled asset type (llassetstorage.h): " + << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << llendl; break; - } - new_listener = new LLFolderBridge(inventory, uuid); - break; - case LLAssetType::AT_LINK: - case LLAssetType::AT_LINK_FOLDER: - // Only should happen for broken links. - new_listener = new LLLinkItemBridge(inventory, uuid); - break; - case LLAssetType::AT_MESH: - if(!(inv_type == LLInventoryType::IT_MESH)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLMeshBridge(inventory, uuid); - break; - default: - llinfos << "Unhandled asset type (llassetstorage.h): " - << (S32)asset_type << llendl; - break; } if (new_listener) @@ -1042,18 +1099,54 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, return new_listener; } +void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) +{ + LLInventoryCategory* cat = model->getCategory(uuid); + if(cat) + { + model->purgeDescendentsOf(uuid); + model->notifyObservers(); + } + LLInventoryObject* obj = model->getObject(uuid); + if (obj) + { + model->purgeObject(uuid); + model->notifyObservers(); + } +} + +// +=================================================+ +// | InventoryFVBridgeBuilder | +// +=================================================+ +LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags /* = 0x00 */) const +{ + return LLInvFVBridge::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + root, + uuid, + flags); +} + // +=================================================+ // | LLItemBridge | // +=================================================+ -void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLItemBridge::performAction(LLInventoryModel* model, std::string action) { - if ("goto" == action) { - gotoItem(folder); + gotoItem(); } - else if ("open" == action) + + if ("open" == action || "open_original" == action) { openItem(); return; @@ -1065,17 +1158,8 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, } else if ("purge" == action) { - LLInventoryCategory* cat = model->getCategory(mUUID); - if(cat) - { - model->purgeDescendentsOf(mUUID); - } - LLInventoryObject* obj = model->getObject(mUUID); - if(!obj) return; - obj->removeFromServer(); - LLPreview::hide(mUUID); - model->deleteObject(mUUID); - model->notifyObservers(); + purgeItem(model, mUUID); + return; } else if ("restoreToWorld" == action) { @@ -1092,11 +1176,11 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, // Single item only LLViewerInventoryItem* item = static_cast(getItem()); if(!item) return; - LLUUID asset_id = item->getAssetUUID(); + LLUUID asset_id = item->getProtectedAssetUUID(); std::string buffer; asset_id.toString(buffer); - gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer)); + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); return; } else if ("copy" == action) @@ -1110,7 +1194,7 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; folder_view_itemp->getListener()->pasteFromClipboard(); @@ -1122,7 +1206,7 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; folder_view_itemp->getListener()->pasteLinkFromClipboard(); @@ -1154,7 +1238,7 @@ void LLItemBridge::showFloaterImagePreview(LLInventoryItem* item, AIFilePicker* void LLItemBridge::selectItem() { LLViewerInventoryItem* item = static_cast(getItem()); - if(item && !item->isComplete()) + if(item && !item->isFinished()) { // if(!(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) @@ -1215,15 +1299,15 @@ void LLItemBridge::restoreToWorld() } } -void LLItemBridge::gotoItem(LLFolderView *folder) +void LLItemBridge::gotoItem() { LLInventoryObject *obj = getInventoryObject(); if (obj && obj->getIsLinkType()) { - LLInventoryView *view = LLInventoryView::getActiveInventory(); - if (view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO); + active_panel->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO); } } } @@ -1245,18 +1329,7 @@ PermissionMask LLItemBridge::getPermissionMask() const { LLViewerInventoryItem* item = getItem(); PermissionMask perm_mask = 0; - if(item) - { - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); - BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); - BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, - gAgent.getID()); - - if (copy) perm_mask |= PERM_COPY; - if (mod) perm_mask |= PERM_MODIFY; - if (xfer) perm_mask |= PERM_TRANSFER; - - } + if (item) perm_mask = item->getPermissionMask(); return perm_mask; } @@ -1286,7 +1359,7 @@ LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const U8 font = LLFontGL::NORMAL; const LLViewerInventoryItem* item = getItem(); - if (get_is_item_worn(item)) + if (get_is_item_worn(mUUID)) { // llinfos << "BOLD" << llendl; font |= LLFontGL::BOLD; @@ -1301,27 +1374,29 @@ LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const std::string LLItemBridge::getLabelSuffix() const { - // *TODO: Translate - static std::string NO_COPY = " (no copy)"; - static std::string NO_MOD = " (no modify)"; - static std::string NO_XFER = " (no transfer)"; - static std::string TEMPO = " (temporary)"; - static std::string LINK = " (link)"; - static std::string BROKEN_LINK = "(broken_link)"; + // String table is loaded before login screen and inventory items are + // loaded after login, so LLTrans should be ready. + static std::string NO_COPY = LLTrans::getString("no_copy"); + static std::string NO_MOD = LLTrans::getString("no_modify"); + static std::string NO_XFER = LLTrans::getString("no_transfer"); + static std::string TEMPO = LLTrans::getString("temporary"); + static std::string LINK = LLTrans::getString("link"); + static std::string BROKEN_LINK = LLTrans::getString("broken_link"); std::string suffix; LLInventoryItem* item = getItem(); if(item) { - // it's a bit confusing to put nocopy/nomod/etc on calling cards. + // Any type can have the link suffix... + BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); + if (broken_link) return BROKEN_LINK; + + BOOL link = item->getIsLinkType(); + if (link) return LINK; + + // ...but it's a bit confusing to put nocopy/nomod/etc suffixes on calling cards. if(LLAssetType::AT_CALLINGCARD != item->getType() && item->getPermissions().getOwner() == gAgent.getID()) { - BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); - if (broken_link) return BROKEN_LINK; - - BOOL link = item->getIsLinkType(); - if (link) return LINK; - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); if (!copy) { @@ -1373,7 +1448,7 @@ BOOL LLItemBridge::isItemRenameable() const return FALSE; } - if (!item->isComplete()) // EXT-8662 + if (!item->isFinished()) // EXT-8662 { return FALSE; } @@ -1428,8 +1503,8 @@ BOOL LLItemBridge::removeItem() if (!item) return FALSE; // Already in trash - if (!model->isObjectDescendentOf(mUUID, trash_id)) - { + if (model->isObjectDescendentOf(mUUID, trash_id)) return FALSE; + // trash problem if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) { @@ -1438,18 +1513,63 @@ BOOL LLItemBridge::removeItem() gInventory.accountForUpdate(up); gInventory.notifyObservers(); } - else // + + LLNotification::Params params("ConfirmItemDeleteHasLinks"); + params.functor(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2)); + + // Check if this item has any links. If generic inventory linking is enabled, + // we can't do this check because we may have items in a folder somewhere that is + // not yet in memory, so we don't want false negatives. (If disabled, then we + // know we only have links in the Outfits folder which we explicitly fetch.) + if (!InventoryLinksEnabled()) + { + if (!item->getIsLinkType()) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLLinkedItemIDMatches is_linked_item_match(mUUID); + gInventory.collectDescendentsIf(gInventory.getRootFolderID(), + cat_array, + item_array, + LLInventoryModel::INCLUDE_TRASH, + is_linked_item_match); + + const U32 num_links = cat_array.size() + item_array.size(); + if (num_links > 0) + { + // Warn if the user is will break any links when deleting this item. + LLNotifications::instance().add(params); + return FALSE; + } + } + } + + LLNotifications::instance().forceResponse(params, 0); + return TRUE; +} + +BOOL LLItemBridge::confirmRemoveItem(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return FALSE; + + LLInventoryModel* model = getInventoryModel(); + if (!model) return FALSE; + + LLViewerInventoryItem* item = getItem(); + if (!item) return FALSE; + + const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + // if item is not already in trash + if(item && !model->isObjectDescendentOf(mUUID, trash_id)) + { // move to trash, and restamp LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE); // delete was successful return TRUE; } - else - { - // tried to delete already item in trash (should purge?) - return FALSE; - } + return FALSE; } BOOL LLItemBridge::isItemCopyable() const @@ -1463,19 +1583,13 @@ BOOL LLItemBridge::isItemCopyable() const return FALSE; }*/ - if(!gHippoGridManager->getConnectedGrid()->supportsInvLinks()) - return (item->getPermissions().allowCopyBy(gAgent.getID())); // You can never copy a link. - else if (item->getIsLinkType()) + if (item->getIsLinkType()) { return FALSE; } - // All items can be copied in god mode since you can - // at least paste-as-link the item, though you - // still may not be able paste the item. - return TRUE; - // return (item->getPermissions().allowCopyBy(gAgent.getID())); + return item->getPermissions().allowCopyBy(gAgent.getID()) || InventoryLinksEnabled(); } return FALSE; } @@ -1518,7 +1632,7 @@ BOOL LLItemBridge::isItemPermissive() const LLHandle LLFolderBridge::sSelf; // Can be moved to another folder -BOOL LLFolderBridge::isItemMovable() +BOOL LLFolderBridge::isItemMovable() const { LLInventoryObject* obj = getInventoryObject(); if(obj) @@ -1533,90 +1647,49 @@ void LLFolderBridge::selectItem() } -// Can be destroyed (or moved to trash) -BOOL LLFolderBridge::isItemRemovable() +// Iterate through a folder's children to determine if +// all the children are removable. +class LLIsItemRemovable : public LLFolderViewFunctor { - LLInventoryModel* model = mInventoryPanel->getModel(); - if(!model) +public: + LLIsItemRemovable() : mPassed(TRUE) {} + virtual void doFolder(LLFolderViewFolder* folder) + { + mPassed &= folder->getListener()->isItemRemovable(); + } + virtual void doItem(LLFolderViewItem* item) + { + mPassed &= item->getListener()->isItemRemovable(); + } + BOOL mPassed; +}; + +// Can be destroyed (or moved to trash) +BOOL LLFolderBridge::isItemRemovable() const +{ + if (!get_is_category_removable(getInventoryModel(), mUUID)) { return FALSE; } - // - // People delete their inventory easily... - if(mUUID == gInventory.getRootFolderID()) + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); + if (folderp) { - return FALSE; - } - if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) - { - return FALSE; - } - -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g - if ( ((rlv_handler_t::isEnabled()) && (RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) && (!RlvFolderLocks::instance().canRemoveFolder(mUUID))) ) - { - return FALSE; - } -// [/RLVa:KB] - - LLVOAvatar* avatar = gAgentAvatarp; - if( !avatar ) - { - return FALSE; - } - - LLInventoryCategory* category = model->getCategory(mUUID); - if( !category ) - { - return FALSE; - } - - if ( (LLFolderType::FT_NONE != category->getPreferredType()) && (LLFolderType::FT_OUTFIT != category->getPreferredType()) ) - { - return FALSE; - } - - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); - - S32 i; - for( i = 0; i < descendent_categories.count(); i++ ) - { - LLInventoryCategory* category = descendent_categories[i]; - if( LLFolderType::FT_NONE != category->getPreferredType() ) + LLIsItemRemovable folder_test; + folderp->applyFunctorToChildren(folder_test); + if (!folder_test.mPassed) { return FALSE; } } - for( i = 0; i < descendent_items.count(); i++ ) - { - LLInventoryItem* item = descendent_items[i]; - if ((item->getType() == LLAssetType::AT_CLOTHING || - item->getType() == LLAssetType::AT_BODYPART) && !item->getIsLinkType()) - { - if( gAgentWearables.isWearingItem( item->getUUID() ) ) - { - return FALSE; - } - } - else if (item->getType() == LLAssetType::AT_OBJECT && !item->getIsLinkType()) - { - if( avatar->isWearingAttachment( item->getUUID() ) ) - { - return FALSE; - } - } - } - return TRUE; } BOOL LLFolderBridge::isUpToDate() const { - LLInventoryModel* model = mInventoryPanel->getModel(); + LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); if( !category ) @@ -1624,12 +1697,68 @@ BOOL LLFolderBridge::isUpToDate() const return FALSE; } - // trying to make it stop trying to fetch Local Inventory return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; - //return (category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) || (mUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)); - // } +BOOL LLFolderBridge::isItemCopyable() const +{ + // Can copy folders to paste-as-link, but not for straight paste. + return InventoryLinksEnabled(); +} + +BOOL LLFolderBridge::copyToClipboard() const +{ + if(isItemCopyable()) + { + LLInventoryClipboard::instance().add(mUUID); + return TRUE; + } + return FALSE; +} + +BOOL LLFolderBridge::isClipboardPasteable() const +{ + if ( ! LLInvFVBridge::isClipboardPasteable() ) + return FALSE; + +/* TODO + // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 + if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) ) + { + LLInventoryModel* model = getInventoryModel(); + if ( !model ) + { + return FALSE; + } + + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + const LLViewerInventoryCategory *current_cat = getCategory(); + + // Search for the direct descendent of current Friends subfolder among all pasted items, + // and return false if is found. + for(S32 i = objects.count() - 1; i >= 0; --i) + { + const LLUUID &obj_id = objects.get(i); + if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) ) + { + return FALSE; + } + } + } +*/ + return TRUE; + + //Old, less restrictive behavior + /*if(LLInventoryClipboard::instance().hasContents() && isAgentInventory()) + { + return TRUE; + } + return FALSE;*/ + +} + + BOOL LLFolderBridge::isClipboardPasteableAsLink() const { // Check normal paste-as-link permissions @@ -1688,6 +1817,49 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const } + +int get_folder_levels(LLInventoryCategory* inv_cat) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + int max_child_levels = 0; + + for (S32 i=0; i < cats->count(); ++i) + { + LLInventoryCategory* category = cats->get(i); + max_child_levels = llmax(max_child_levels, get_folder_levels(category)); + } + + return 1 + max_child_levels; +} + +int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) +{ + int depth = 0; + + if (ancestor_id == descendant_id) return depth; + + const LLInventoryCategory* category = gInventory.getCategory(descendant_id); + + while(category) + { + LLUUID parent_id = category->getParentUUID(); + + if (parent_id.isNull()) break; + + depth++; + + if (parent_id == ancestor_id) return depth; + + category = gInventory.getCategory(parent_id); + } + + llwarns << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << llendl; + return -1; +} + BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL drop) { @@ -1699,77 +1871,170 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (!isAgentAvatarValid()) return FALSE; if (!isAgentInventory()) return FALSE; // cannot drag categories into library + const LLUUID &cat_id = inv_cat->getUUID(); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); // check to make sure source is agent inventory, and is represented there. LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); - BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL) + const BOOL is_agent_inventory = (model->getCategory(cat_id) != NULL) && (LLToolDragAndDrop::SOURCE_AGENT == source); BOOL accept = FALSE; - if(is_agent_inventory) + if (is_agent_inventory) { - const LLUUID& cat_id = inv_cat->getUUID(); const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); + const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); - BOOL is_movable = (LLFolderType::FT_NONE == inv_cat->getPreferredType()); - if( is_movable ) + const BOOL move_is_into_outfit = getCategory() && (getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); + const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); + + //-------------------------------------------------------------------------------- + // Determine if folder can be moved. + // + + BOOL is_movable = TRUE; + + // Can't move a folder into itself + if (is_movable && (mUUID == cat_id)) { - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE ); + is_movable = FALSE; + // tooltip? + } + // Avoid circularity + if (is_movable && (model->isObjectDescendentOf(mUUID, cat_id))) + { + is_movable = FALSE; + // tooltip? + } + // Can't move protected types. + if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + is_movable = FALSE; + // tooltip? + } + // Can't drag into the outfit folder + if (is_movable && move_is_into_outfit) + { + is_movable = FALSE; + // tooltip? + } + // Can't move favorites folder. + if (is_movable && (mUUID == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) + { + is_movable = FALSE; + // tooltip? + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + if (is_movable) + { + model->collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); for (S32 i=0; i < descendent_categories.count(); ++i) { LLInventoryCategory* category = descendent_categories[i]; if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) { - // ...can't move "special folders" like Textures + // Can't move "special folders" (e.g. Textures Folder). is_movable = FALSE; break; } } - if( is_movable && move_is_into_trash ) + } + if (is_movable && move_is_into_trash) + { + for (S32 i=0; i < descendent_items.count(); ++i) { - for (S32 i=0; i < descendent_items.count(); ++i) + LLInventoryItem* item = descendent_items[i]; + if (get_is_item_worn(item->getUUID())) + { + is_movable = FALSE; + break; // It's generally movable, but not into the trash. + } + } + } + if (is_movable && move_is_into_landmarks) + { + for (S32 i=0; i < descendent_items.count(); ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + + // Don't move anything except landmarks and categories into Landmarks folder. + // We use getType() instead of getActua;Type() to allow links to landmarks and folders. + if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType()) + { + is_movable = FALSE; + break; // It's generally movable, but not into Landmarks. + } + } + } + + accept = is_movable; + + if (accept && drop) + { + // Look for any gestures and deactivate them + if (move_is_into_trash) + { + for (S32 i=0; i < descendent_items.count(); i++) { LLInventoryItem* item = descendent_items[i]; - if (get_is_item_worn(item)) + if (item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(item->getUUID())) { - is_movable = FALSE; // It's generally movable, but not into the trash! - break; + LLGestureMgr::instance().deactivateGesture(item->getUUID()); } } } - - // - //-------------------------------------------------------------------------------- - -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Added: RLVa-1.3.0g - if ( (is_movable) && (rlv_handler_t::isEnabled()) && (RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) ) + // if target is an outfit or current outfit folder we use link + if (move_is_into_current_outfit || move_is_into_outfit) { - is_movable = RlvFolderLocks::instance().canMoveFolder(cat_id, mUUID); - } -// [/RLVa:KB] - - accept = is_movable - && (mUUID != cat_id) // Can't move a folder into itself - && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing - && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity - if (accept && drop) - { - // Look for any gestures and deactivate them - if (move_is_into_trash) + if (inv_cat->getPreferredType() == LLFolderType::FT_NONE) { - for (S32 i = 0; i < descendent_items.count(); i++) + if (move_is_into_current_outfit) { - LLInventoryItem* item = descendent_items[i]; - if (item->getType() == LLAssetType::AT_GESTURE - && LLGestureMgr::instance().isGestureActive(item->getUUID())) - { - LLGestureMgr::instance().deactivateGesture(item->getUUID()); - } + // traverse category and add all contents to currently worn. + BOOL append = true; + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); + } + else + { + // Recursively create links in target outfit. + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + model->collectDescendents(cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + LLAppearanceMgr::instance().linkAll(mUUID,items,NULL); } } - + else + { +#if SUPPORT_ENSEMBLES + // BAP - should skip if dup. + if (move_is_into_current_outfit) + { + LLAppearanceMgr::instance().addEnsembleLink(inv_cat); + } + else + { + LLPointer cb = NULL; + const std::string empty_description = ""; + link_inventory_item( + gAgent.getID(), + cat_id, + mUUID, + inv_cat->getName(), + empty_description, + LLAssetType::AT_LINK_FOLDER, + cb); + } +#endif + } + } + else + { // Reparent the folder and restamp children if it's moving // into trash. LLInvFVBridge::changeCategoryParent( @@ -1780,13 +2045,21 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } - else if(LLToolDragAndDrop::SOURCE_WORLD == source) + else if (LLToolDragAndDrop::SOURCE_WORLD == source) { - // content category has same ID as object itself - LLUUID object_id = inv_cat->getUUID(); - LLUUID category_id = mUUID; - accept = move_inv_category_world_to_agent(object_id, category_id, drop); + accept = move_inv_category_world_to_agent(cat_id, mUUID, drop); } + else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + // Accept folders that contain complete outfits. + accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id); + + if (accept && drop) + { + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false); + } + } + return accept; } @@ -1921,7 +2194,6 @@ public: gInventory.removeObserver(this); delete this; } - protected: LLUUID mCatID; @@ -2062,7 +2334,6 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask) if (mFolderAdded) { LLViewerInventoryCategory* category = gInventory.getCategory(mCatID); - if (NULL == category) { llwarns << "gInventory.getCategory(" << mCatID @@ -2074,7 +2345,7 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask) mContentsCount) { gInventory.removeObserver(this); - wear_inventory_category(category, FALSE, TRUE); + LLAppearanceMgr::instance().wearInventoryCategory(category, FALSE, FALSE); delete this; } } @@ -2085,11 +2356,16 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask) -void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if ("open" == action) { - openItem(); + LLFolderViewFolder *f = dynamic_cast(mRoot->getItemByID(mUUID)); + if (f) + { + f->setOpen(TRUE); + } + return; } else if ("paste" == action) @@ -2117,9 +2393,14 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model modifyOutfit(TRUE); return; } + else if ("copy" == action) + { + copyToClipboard(); + return; + } else if ("wearitems" == action) { - modifyOutfit(TRUE, TRUE); + modifyOutfit(TRUE); } else if ("removefromoutfit" == action) { @@ -2132,19 +2413,9 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model return; } else if ("purge" == action) - { - LLViewerInventoryCategory* cat; - cat = (LLViewerInventoryCategory*)getCategory(); - - if(cat) - { - model->purgeDescendentsOf(mUUID); - } - LLInventoryObject* obj = model->getObject(mUUID); - if(!obj) return; - obj->removeFromServer(); - model->deleteObject(mUUID); - model->notifyObservers(); + { + purgeItem(model, mUUID); + return; } else if ("restore" == action) { @@ -2169,6 +2440,24 @@ void LLFolderBridge::openItem() } } +void LLFolderBridge::closeItem() +{ + determineFolderType(); +} + +void LLFolderBridge::determineFolderType() +{ + if (isUpToDate()) + { + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryCategory* category = model->getCategory(mUUID); + if (category) + { + category->determineFolderType(); + } + } +} + BOOL LLFolderBridge::isItemRenameable() const { return get_is_category_renameable(getInventoryModel(), mUUID); @@ -2241,80 +2530,48 @@ BOOL LLFolderBridge::removeItem() { return FALSE; } - // move it to the trash - LLPreview::hide(mUUID); - LLInventoryModel* model = mInventoryPanel->getModel(); - if(!model) return FALSE; - - LLUUID trash_id; - trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - - // Look for any gestures and deactivate them - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); - - S32 i; - for (i = 0; i < descendent_items.count(); i++) - { - LLInventoryItem* item = descendent_items[i]; - if (item->getType() == LLAssetType::AT_GESTURE - && LLGestureMgr::instance().isGestureActive(item->getUUID())) - { - LLGestureMgr::instance().deactivateGesture(item->getUUID()); - } - } - - // go ahead and do the normal remove if no 'last calling - // cards' are being removed. - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - // trash problem - if(gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot)) - { - S32 descendents = cat->getDescendentCount(); - if(descendents > 0) - { - LLInventoryModel::LLCategoryUpdate up(cat->getUUID(), -descendents); - gInventory.accountForUpdate(up); - } - cat->setDescendentCount(0); - LLInventoryModel::cat_array_t categories; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(cat->getUUID(), - categories, - items, - false); // include trash? - S32 count = items.count(); - S32 i; - for(i = 0; i < count; ++i) - { - gInventory.deleteObject(items.get(i)->getUUID()); - } - count = categories.count(); - for(i = 0; i < count; ++i) - { - gInventory.deleteObject(categories.get(i)->getUUID()); - } - - LLInventoryModel::LLCategoryUpdate up(cat->getParentUUID(), -descendents); - gInventory.deleteObject(cat->getUUID()); - gInventory.accountForUpdate(up); - gInventory.notifyObservers(); - } - else - // - LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE); - } + const LLViewerInventoryCategory *cat = getCategory(); + + LLSD payload; + LLSD args; + args["FOLDERNAME"] = cat->getName(); + LLNotification::Params params("ConfirmDeleteProtectedCategory"); + params.payload(payload).substitutions(args).functor(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2)); + LLNotifications::instance().forceResponse(params, 0); return TRUE; } -BOOL LLFolderBridge::isClipboardPasteable() const +BOOL LLFolderBridge::removeSystemFolder() { - if(LLInventoryClipboard::instance().hasContents() && isAgentInventory()) + const LLViewerInventoryCategory *cat = getCategory(); + if (!LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { + return FALSE; + } + + LLSD payload; + LLSD args; + args["FOLDERNAME"] = cat->getName(); + + LLNotification::Params params("ConfirmDeleteProtectedCategory"); + params.payload(payload).substitutions(args).functor(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2)); + { + LLNotifications::instance().add(params); + } + return TRUE; +} + +bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + // if they choose delete, do it. Otherwise, don't do anything + if(option == 0) + { + // move it to the trash + LLPreview::hide(mUUID); + remove_category(getInventoryModel(), mUUID); return TRUE; } return FALSE; @@ -2325,6 +2582,10 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model && isClipboardPasteable()) { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const LLUUID parent_id(mUUID); LLDynamicArray objects; @@ -2337,10 +2598,29 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryItem *item = model->getItem(item_id); if (item) { + if (move_is_into_current_outfit || move_is_into_outfit) { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), + if (can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if(LLInventoryClipboard::instance().isCutMode()) + { + // move_inventory_item() is not enough, + //we have to update inventory locally too + LLViewerInventoryItem* viitem = dynamic_cast(item); + llassert(viitem); + if (viitem) + { + changeItemParent(model, viitem, parent_id, FALSE); + } + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), item->getUUID(), parent_id, std::string(), @@ -2356,6 +2636,10 @@ void LLFolderBridge::pasteLinkFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model) { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const LLUUID parent_id(mUUID); LLDynamicArray objects; @@ -2365,7 +2649,27 @@ void LLFolderBridge::pasteLinkFromClipboard() ++iter) { const LLUUID &object_id = (*iter); - if (LLInventoryItem *item = model->getItem(object_id)) + if (move_is_into_current_outfit || move_is_into_outfit) + { + LLInventoryItem *item = model->getItem(object_id); + if (item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if (LLInventoryCategory *cat = model->getCategory(object_id)) + { + const std::string empty_description = ""; + link_inventory_item( + gAgent.getID(), + cat->getUUID(), + parent_id, + cat->getName(), + empty_description, + LLAssetType::AT_LINK_FOLDER, + LLPointer(NULL)); + } + else if (LLInventoryItem *item = model->getItem(object_id)) { link_inventory_item( gAgent.getID(), @@ -2383,13 +2687,129 @@ void LLFolderBridge::pasteLinkFromClipboard() void LLFolderBridge::staticFolderOptionsMenu() { LLFolderBridge* selfp = sSelf.get(); - if (selfp) + + if (selfp && selfp->mRoot) { - selfp->folderOptionsMenu(); + selfp->mRoot->updateMenu(); } } -void LLFolderBridge::folderOptionsMenu() +BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendentsIf(mUUID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_type); + return ((item_array.count() > 0) ? TRUE : FALSE ); +} + +void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) +{ + LLInventoryModel* model = getInventoryModel(); + llassert(model != NULL); + + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + +// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) + // Fixes LL bug + mItems.clear(); + mDisabledItems.clear(); +// [/RLVa:KB] + + if (lost_and_found_id == mUUID) + { + // This is the lost+found folder. + mItems.push_back(std::string("Empty Lost And Found")); + + mDisabledItems.push_back(std::string("New Folder")); + mDisabledItems.push_back(std::string("New Script")); + mDisabledItems.push_back(std::string("New Note")); + mDisabledItems.push_back(std::string("New Gesture")); + mDisabledItems.push_back(std::string("New Clothes")); + mDisabledItems.push_back(std::string("New Body Parts")); + } + + if(trash_id == mUUID) + { + // This is the trash. + mItems.push_back(std::string("Empty Trash")); + } + else if(isItemInTrash()) + { + // This is a folder in the trash. + mItems.clear(); // clear any items that used to exist + addTrashContextMenuOptions(mItems, mDisabledItems); + } + else if(isAgentInventory()) // do not allow creating in library + { + LLViewerInventoryCategory *cat = getCategory(); + // BAP removed protected check to re-enable standard ops in untyped folders. + // Not sure what the right thing is to do here. + if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) + { + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + if(panel && !panel->getFilterWorn()) + { + mItems.push_back(std::string("New Folder")); + mItems.push_back(std::string("New Script")); + mItems.push_back(std::string("New Note")); + mItems.push_back(std::string("New Gesture")); + mItems.push_back(std::string("New Clothes")); + mItems.push_back(std::string("New Body Parts")); + } + + getClipboardEntries(false, mItems, mDisabledItems, flags); + } + else + { + // Want some but not all of the items from getClipboardEntries for outfits. + if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + mItems.push_back(std::string("Rename")); + + addDeleteContextMenuOptions(mItems, mDisabledItems); + // EXT-4030: disallow deletion of currently worn outfit + const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) + { + mDisabledItems.push_back(std::string("Delete")); + } + } + } + + //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06 + mCallingCards = mWearables = FALSE; + + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (checkFolderForContentsOfType(model, is_callingcard)) + { + mCallingCards=TRUE; + } + + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + mWearables=TRUE; + } + } + + // Preemptively disable system folder removal if more than one item selected. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + mDisabledItems.push_back(std::string("Delete System Folder")); + } +} + +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) { // *TODO: Translate @@ -2425,6 +2845,13 @@ void LLFolderBridge::folderOptionsMenu() mDisabledItems.push_back(std::string("Delete")); } +#ifndef LL_RELEASE_FOR_DOWNLOAD + if (LLFolderType::lookupIsProtectedType(type)) + { + mItems.push_back(std::string("Delete System Folder")); + } +#endif + // wearables related functionality for folders. //is_wearable LLFindWearables is_wearable; @@ -2441,39 +2868,34 @@ void LLFolderBridge::folderOptionsMenu() // Only enable add/replace outfit for non-default folders. if (!is_system_folder) { - if (gHippoGridManager->getConnectedGrid()->supportsInvLinks() && + if (InventoryLinksEnabled() && // Adding an outfit onto another (versus replacing) doesn't make sense. type != LLFolderType::FT_OUTFIT) - { + { mItems.push_back(std::string("Add To Outfit")); } - mItems.push_back(std::string("Wear Items")); + else if(!InventoryLinksEnabled()) + mItems.push_back(std::string("Wearable And Object Wear")); mItems.push_back(std::string("Replace Outfit")); } - mItems.push_back(std::string("Take Off Items")); + mItems.push_back(std::string("Remove From Outfit")); + if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) + { + mDisabledItems.push_back(std::string("Remove From Outfit")); + } + if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) + { + mDisabledItems.push_back(std::string("Replace Outfit")); + } + mItems.push_back(std::string("Outfit Separator")); } - LLMenuGL* menup = dynamic_cast(mMenu.get()); - if (menup) - { - hide_context_entries(*menup, mItems, mDisabledItems); - } -} - -BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type) -{ - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - model->collectDescendentsIf(mUUID, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_type); - return ((item_array.count() > 0) ? TRUE : FALSE ); } // Flags unused void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { + sSelf.markDead(); + mItems.clear(); mDisabledItems.clear(); @@ -2482,125 +2904,36 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // std::vector disabled_items; LLInventoryModel* model = getInventoryModel(); if(!model) return; - LLUUID cof_id = LLCOFMgr::instance().getCOF(); - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - -// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) - // Fixes LL bug - mItems.clear(); - mDisabledItems.clear(); -// [/RLVa:KB] - - if (lost_and_found_id == mUUID) - { - // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); - } - // clear out old menu and folder pointers - mMenu.markDead(); - sSelf.markDead(); - if (cof_id == mUUID) + buildContextMenuBaseOptions(flags); + + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category) { - mItems.push_back(std::string("Take Off Items")); - } - else if(trash_id == mUUID) - { - // This is the trash. - mItems.push_back(std::string("Empty Trash")); - } - else if(isItemInTrash()) - { - // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - addTrashContextMenuOptions(mItems, mDisabledItems); - } - else if(isAgentInventory()) // do not allow creating in library - { - //LLViewerInventoryCategory *cat = getCategory(); - // BAP removed protected check to re-enable standard ops in untyped folders. - // Not sure what the right thing is to do here. - //if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) - { - mItems.push_back(std::string("New Folder")); - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); - - getClipboardEntries(false, mItems, mDisabledItems, flags); - } - /*else - { - // Want some but not all of the items from getClipboardEntries for outfits. - if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) - { - mItems.push_back(std::string("Rename")); - - addDeleteContextMenuOptions(mItems, mDisabledItems); - // EXT-4030: disallow deletion of currently worn outfit - const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); - if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) - { - mDisabledItems.push_back(std::string("Delete")); - } - } - }*/ - - //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06 - mCallingCards = mWearables = FALSE; - - LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); - if (checkFolderForContentsOfType(model, is_callingcard)) - { - mCallingCards=TRUE; - } - - LLFindWearables is_wearable; - LLIsType is_object( LLAssetType::AT_OBJECT ); - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - - if (checkFolderForContentsOfType(model, is_wearable) || - checkFolderForContentsOfType(model, is_object) || - checkFolderForContentsOfType(model, is_gesture) ) - { - mWearables=TRUE; - } - - uuid_vec_t folders; - LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); - if (category) - { - folders.push_back(category->getUUID()); - } + folders.push_back(category->getUUID()); - mMenu = menu.getHandle(); sSelf = getHandle(); LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders, FALSE); fetch->startFetch(); inc_busy_count(); - if(fetch->isFinished()) + if (fetch->isFinished()) { - // everything is already here - call done. - fetch->done(); + buildContextMenuFolderOptions(flags); } else { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. gInventory.addObserver(fetch); } } - else - { - mItems.push_back(std::string("--no options--")); - mDisabledItems.push_back(std::string("--no options--")); - } hide_context_entries(menu, mItems, mDisabledItems); + + // Reposition the menu, in case we're adding items to an existing menu. + menu.needsArrange(); + menu.arrangeAndClear(); } BOOL LLFolderBridge::hasChildren() const @@ -2622,27 +2955,45 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, BOOL accept = FALSE; switch(cargo_type) { - case DAD_TEXTURE: - case DAD_SOUND: - case DAD_CALLINGCARD: - case DAD_LANDMARK: - case DAD_SCRIPT: - case DAD_OBJECT: - case DAD_NOTECARD: - case DAD_CLOTHING: - case DAD_BODYPART: - case DAD_ANIMATION: - case DAD_GESTURE: - case DAD_LINK: - accept = dragItemIntoFolder(inv_item, - drop); - break; - case DAD_CATEGORY: - accept = dragCategoryIntoFolder((LLInventoryCategory*)inv_item, - drop); - break; - default: - break; + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_CALLINGCARD: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: + accept = dragItemIntoFolder(inv_item, drop); + break; + case DAD_LINK: + // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. + // If we have an item of AT_LINK_FOLDER type we should process the linked + // category being dragged or dropped into folder. + if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType()) + { + LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); + if (linked_category) + { + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop); + } + } + else + { + accept = dragItemIntoFolder(inv_item, drop); + } + break; + case DAD_CATEGORY: + accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop); + case DAD_ROOT_CATEGORY: + case DAD_NONE: + break; + default: + llwarns << "Unhandled cargo type for drag&drop " << cargo_type << llendl; + break; } return accept; } @@ -2670,7 +3021,7 @@ void LLFolderBridge::createNewCategory(void* user_data) { LLFolderBridge* bridge = (LLFolderBridge*)user_data; if(!bridge) return; - LLInventoryPanel* panel = dynamic_cast(bridge->mInventoryPanel); + LLInventoryPanel* panel = dynamic_cast(bridge->mInventoryPanel.get()); if (!panel) return; LLInventoryModel* model = panel->getModel(); if(!model) return; @@ -2769,32 +3120,17 @@ void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::ETyp { if(!bridge) return; LLUUID parent_id = bridge->getUUID(); - createWearable(parent_id, type); + LLAgentWearables::createWearable(type, false, parent_id); } -// Separate function so can be called by global menu as well as right-click -// menu. -// static -void LLFolderBridge::createWearable(LLUUID parent_id, LLWearableType::EType type) -{ - LLWearable* wearable = LLWearableList::instance().createNewWearable(type); - LLAssetType::EType asset_type = wearable->getAssetType(); - LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), - parent_id, wearable->getTransactionID(), wearable->getName(), - wearable->getDescription(), asset_type, inv_type, wearable->getType(), - wearable->getPermissions().getMaskNextOwner(), - LLPointer(NULL)); -} - -void LLFolderBridge::modifyOutfit(BOOL append, BOOL replace) +void LLFolderBridge::modifyOutfit(BOOL append/*, BOOL replace*/) { LLInventoryModel* model = getInventoryModel(); if(!model) return; LLViewerInventoryCategory* cat = getCategory(); if(!cat) return; - - wear_inventory_category_on_avatar(cat, append, replace); + + LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, append ); } // helper stuff @@ -2811,8 +3147,8 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response LLInventoryObject::object_list_t inventory_objects; object->getInventoryContents(inventory_objects); int contents_count = inventory_objects.size()-1; //subtract one for containing folder - LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded); + gInventory.addObserver(inventoryObserver); } @@ -2837,6 +3173,88 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response return false; } +// Returns true if the item can be moved to Current Outfit or any outfit folder. +static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +{ + if ((inv_item->getInventoryType() != LLInventoryType::IT_WEARABLE) && + (inv_item->getInventoryType() != LLInventoryType::IT_GESTURE) && + (inv_item->getInventoryType() != LLInventoryType::IT_ATTACHMENT) && + (inv_item->getInventoryType() != LLInventoryType::IT_OBJECT)) + { + return FALSE; + } + + if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) + { + return FALSE; + } + + return TRUE; +} + +// Returns TRUE if item is a landmark or a link to a landmark +// and can be moved to Favorites or Landmarks folder. +static BOOL can_move_to_landmarks(LLInventoryItem* inv_item) +{ + // Need to get the linked item to know its type because LLInventoryItem::getType() + // returns actual type AT_LINK for links, not the asset type of a linked item. + if (LLAssetType::AT_LINK == inv_item->getType()) + { + LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); + if (linked_item) + { + return LLAssetType::AT_LANDMARK == linked_item->getType(); + } + } + + return LLAssetType::AT_LANDMARK == inv_item->getType(); +} + +void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) +{ + // use callback to rearrange favorite landmarks after adding + // to have new one placed before target (on which it was dropped). See EXT-4312. + LLPointer cb = new AddFavoriteLandmarkCallback(); + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; + if (drag_over_item && drag_over_item->getListener()) + { + cb.get()->setTargetLandmarkId(drag_over_item->getListener()->getUUID()); + } + + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + mUUID, + std::string(), + cb); +} + +void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +{ + // BAP - should skip if dup. + if (move_is_into_current_outfit) + { + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); + } + else + { + LLPointer cb = NULL; + link_inventory_item( + gAgent.getID(), + inv_item->getLinkedUUID(), + mUUID, + inv_item->getName(), + inv_item->getDescription(), + LLAssetType::AT_LINK, + cb); + } +} + +// This is used both for testing whether an item can be dropped +// into the folder, as well as performing the actual drop, depending +// if drop == TRUE. BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop) { @@ -2847,7 +3265,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (!isAgentAvatarValid()) return FALSE; const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_favorites = (mUUID == favorites_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); BOOL accept = FALSE; @@ -2857,7 +3281,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); - const BOOL move_is_outof_current_outfit = model->isObjectDescendentOf(inv_item->getUUID(), current_outfit_id); + const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); + //-------------------------------------------------------------------------------- // Determine if item can be moved. // @@ -2877,33 +3302,52 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, break; } - if (is_movable && move_is_outof_current_outfit) + if (move_is_outof_current_outfit) { - is_movable = FALSE; // Don't allow dragging links out of COF + is_movable = FALSE; } - else if(is_movable && move_is_into_trash) + if (move_is_into_trash) { is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID()); } - -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g - if ( (rlv_handler_t::isEnabled()) && (is_movable) ) - { - if (move_is_into_current_outfit) - { - // RELEASE-RLVa: [RLVa-1.3.0] Keep sync'ed with code below => LLAppearanceMgr::wearItemOnAvatar() with "replace == true" - const LLViewerInventoryItem* pItem = dynamic_cast(inv_item); - is_movable = rlvPredCanWearItem(pItem, RLV_WEAR_REPLACE); - } - if (is_movable) - { - is_movable = (!RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) || - (RlvFolderLocks::instance().canMoveItem(inv_item->getUUID(), mUUID)); - } - } - // [/RLVa:KB] +/* TODO + if (is_movable) + { + // Don't allow creating duplicates in the Calling Card/Friends + // subfolders, see bug EXT-1599. Check is item direct descendent + // of target folder and forbid item's movement if it so. + // Note: isItemDirectDescendentOfCategory checks if + // passed category is in the Calling Card/Friends folder + is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, getCategory()); + } +*/ + + // + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // Determine if item can be moved & dropped + // + + accept = TRUE; + + if (!is_movable) + { + accept = FALSE; + } + else if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + { + accept = FALSE; + } + else if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + else if (move_is_into_favorites || move_is_into_landmarks) + { + accept = can_move_to_landmarks(inv_item); + } - accept = is_movable && (mUUID != inv_item->getParentUUID()); if(accept && drop) { if (inv_item->getType() == LLAssetType::AT_GESTURE @@ -2911,41 +3355,35 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { LLGestureMgr::instance().deactivateGesture(inv_item->getUUID()); } - // If an item is being dragged between windows, unselect - // everything in the active window so that we don't follow - // the selection to its new location (which is very - // annoying). - if (LLInventoryView::getActiveInventory()) + // If an item is being dragged between windows, unselect everything in the active window + // so that we don't follow the selection to its new location (which is very annoying). + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + if (active_panel) { - LLInventoryPanel* active_panel = LLInventoryView::getActiveInventory()->getPanel(); - if (active_panel && (mInventoryPanel != active_panel)) + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + if (active_panel && (panel != active_panel)) { active_panel->unSelectAll(); } } - if (move_is_into_current_outfit) + // FAVORITES folder + // (copy the item) + if (move_is_into_favorites) { - switch (inv_item->getType()) - { - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - wear_inventory_item_on_avatar(inv_item); - break; - case LLAssetType::AT_OBJECT: - rez_attachment((LLViewerInventoryItem*)inv_item, NULL, true); - break; - /* - case LLAssetType::AT_GESTURE: - break; - */ - default: - break; - } + dropToFavorites(inv_item); } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + dropToOutfit(inv_item, move_is_into_current_outfit); + } + // NORMAL or TRASH folder + // (move the item, restamp if into trash) else { - // restamp if the move is into the trash. + LLInvFVBridge::changeItemParent( model, (LLViewerInventoryItem*)inv_item, @@ -2955,7 +3393,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // //-------------------------------------------------------------------------------- - } } else if(LLToolDragAndDrop::SOURCE_WORLD == source) @@ -2976,8 +3413,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL is_move = FALSE; if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) && perm.allowTransferTo(gAgent.getID()))) -// || gAgent.isGodlike()) - + // || gAgent.isGodlike()) { accept = TRUE; } @@ -2989,7 +3425,22 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, is_move = TRUE; accept = TRUE; } - if(drop && accept) + + // Don't allow placing an original item into Current Outfit or an outfit folder + // because they must contain only links to wearable items. + // *TODO: Probably we should create a link to an item if it was dragged to outfit or COF. + if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = FALSE; + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if ((move_is_into_favorites || move_is_into_landmarks) + && !can_move_to_landmarks(inv_item)) + { + accept = FALSE; + } + if (accept && drop) { LLMoveInv* move_inv = new LLMoveInv; move_inv->mObjectID = inv_item->getParentUUID(); @@ -3003,6 +3454,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } else { + // store dad inventory item to select added one later. See EXT-4347 + set_dad_inventory_item(inv_item, mUUID); LLNotification::Params params("MoveInventoryFromObject"); params.functor(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); LLNotifications::instance().forceResponse(params, 0); @@ -3012,28 +3465,61 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) { - accept = TRUE; - if(drop) { - copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(), - LLToolDragAndDrop::getInstance()->getSourceID(), inv_item); + // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder + // because they must contain only links to wearable items. + accept = !(move_is_into_current_outfit || move_is_into_outfit); + } + + if (accept && drop) + { + copy_inventory_from_notecard(mUUID, // Drop to the chosen destination folder + LLToolDragAndDrop::getInstance()->getObjectID(), + LLToolDragAndDrop::getInstance()->getSourceID(), + inv_item); } } else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) { LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; - if(item && item->isComplete()) + if(item && item->isFinished()) { accept = TRUE; - if(drop) + if (move_is_into_current_outfit || move_is_into_outfit) { - copy_inventory_item( - gAgent.getID(), - inv_item->getPermissions().getOwner(), - inv_item->getUUID(), - mUUID, - std::string(), - LLPointer(NULL)); + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if (move_is_into_favorites || move_is_into_landmarks) + { + accept = can_move_to_landmarks(inv_item); + } + + if (accept && drop) + { + // FAVORITES folder + // (copy the item) + if (move_is_into_favorites) + { + dropToFavorites(inv_item); + } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + dropToOutfit(inv_item, move_is_into_current_outfit); + } + else + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + mUUID, + std::string(), + LLPointer(NULL)); + } } } } @@ -3093,40 +3579,68 @@ void open_texture(const LLUUID& item_id, void LLTextureBridge::openItem() { LLViewerInventoryItem* item = getItem(); - if(item) + + if (item) { - open_texture(mUUID, getPrefix() + item->getName(), FALSE); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } } +// virtual +void LLTextureBridge::performAction(LLInventoryModel* model, std::string action) +{ + // TODO + /*if ("save_as" == action) + { + LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES); + LLPreviewTexture* preview_texture = LLFloaterReg::findTypedInstance("preview_texture", mUUID); + if (preview_texture) + { + preview_texture->openToSave(); + } + } + else*/ + LLItemBridge::performAction(model, action); +} + +void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLTextureBridge::buildContextMenu()" << llendl; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + //TODO + /*items.push_back(std::string("Texture Separator")); + items.push_back(std::string("Save As")); + if (!canSaveTexture()) + { + disabled_items.push_back(std::string("Save As")); + }*/ + } + hide_context_entries(menu, items, disabled_items); +} + + // +=================================================+ // | LLSoundBridge | // +=================================================+ void LLSoundBridge::openItem() { -// Changed this back to the way it USED to work: -// only open the preview dialog through the contextual right-click menu -// double-click just plays the sound - LLViewerInventoryItem* item = getItem(); if(item) { - openSoundPreview((void*)this); - //send_uuid_sound_trigger(item->getAssetUUID(), 1.0); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } - -// if(!LLPreview::show(mUUID)) -// { -// S32 left, top; -// gFloaterView->getNewFloaterPosition(&left, &top); -// LLRect rect = gSavedSettings.getRect("PreviewSoundRect"); -// rect.translate(left - rect.mLeft, top - rect.mTop); -// new LLPreviewSound("preview sound", -// rect, -// getPrefix() + getName(), -// mUUID)); -// } } void LLSoundBridge::previewItem() @@ -3149,7 +3663,7 @@ void LLSoundBridge::openSoundPreview(void* which) rect.translate(left - rect.mLeft, top - rect.mTop); LLPreviewSound* preview = new LLPreviewSound("preview sound", rect, - me->getPrefix() + me->getName(), + std::string("Sound: ") + me->getName(), me->mUUID); preview->setFocus(TRUE); // Keep entirely onscreen. @@ -3159,9 +3673,9 @@ void LLSoundBridge::openSoundPreview(void* which) void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - lldebugs << "LLTextureBridge::buildContextMenu()" << llendl; - std::vector items; - std::vector disabled_items; + lldebugs << "LLSoundBridge::buildContextMenu()" << llendl; + menuentry_vec_t items; + menuentry_vec_t disabled_items; // *TODO: Translate if(isItemInTrash()) @@ -3187,10 +3701,10 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // +=================================================+ LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid, U32 flags/* = 0x00*/) : - LLItemBridge(inventory, /*root,*/ uuid) + LLItemBridge(inventory, root, uuid) { mVisited = FALSE; if (flags & LLInventoryItemFlags::II_FLAGS_LANDMARK_VISITED) @@ -3226,38 +3740,54 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Landmark Separator")); items.push_back(std::string("Teleport To Landmark")); + // Disable "About Landmark" menu item for + // multiple landmarks selected. Only one landmark + // info panel can be shown at a time. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Teleport To Landmark")); + } hide_context_entries(menu, items, disabled_items); +} +// Convenience function for the two functions below. +void teleport_via_landmark(const LLUUID& asset_id, const LLUUID& item_id) +{ + gAgent.teleportViaLandmark( asset_id ); + + // we now automatically track the landmark you're teleporting to + // because you'll probably arrive at a telehub instead + LLFloaterWorldMap* floater_world_map = gFloaterWorldMap; + if( floater_world_map ) + { + //Emerald says this needs to be the item id instead of the asset ID. -HgB + floater_world_map->trackLandmark( item_id ); + } } // virtual -void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLLandmarkBridge::performAction(LLInventoryModel* model, std::string action) { if ("teleport" == action) { LLViewerInventoryItem* item = getItem(); if(item) { - gAgent.teleportViaLandmark(item->getAssetUUID()); - - // we now automatically track the landmark you're teleporting to - // because you'll probably arrive at a telehub instead - if( gFloaterWorldMap ) - { - //Emerald claims this needs to be the item UUID, rather than the asset UUID. -HgB - gFloaterWorldMap->trackLandmark( item->getUUID() ); - } + teleport_via_landmark(item->getAssetUUID(), item->getUUID()); } } - if ("about" == action) + else if ("about" == action) { LLViewerInventoryItem* item = getItem(); if(item) { - open_landmark(item, std::string(" ") + getPrefix() + item->getName(), FALSE); + open_landmark(item, std::string(" Landmark: ") + item->getName(), FALSE); } } - else LLItemBridge::performAction(folder, model, action); + else + { + LLItemBridge::performAction(model, action); + } } void open_landmark(LLViewerInventoryItem* inv_item, @@ -3296,16 +3826,7 @@ static bool open_landmark_callback(const LLSD& notification, const LLSD& respons LLUUID item_id = notification["payload"]["item_id"].asUUID(); if (option == 0) { - // HACK: This is to demonstrate teleport on double click for landmarks - gAgent.teleportViaLandmark( asset_id ); - - // we now automatically track the landmark you're teleporting to - // because you'll probably arrive at a telehub instead - if( gFloaterWorldMap ) - { - //Emerald says this needs to be the item id instead of the asset ID. -HgB - gFloaterWorldMap->trackLandmark( item_id ); - } + teleport_via_landmark( asset_id, item_id ); } return false; @@ -3316,15 +3837,10 @@ static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFro void LLLandmarkBridge::openItem() { LLViewerInventoryItem* item = getItem(); - if( item ) + + if (item) { - // Opening (double-clicking) a landmark immediately teleports, - // but warns you the first time. - // open_landmark(item, std::string(" ") + getPrefix() + item->getName(), FALSE); - LLSD payload; - payload["asset_id"] = item->getAssetUUID(); - payload["item_id"] = item->getUUID(); - LLNotificationsUtil::add("TeleportFromLandmark", LLSD(), payload); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } } @@ -3350,9 +3866,9 @@ protected: // +=================================================+ LLCallingCardBridge::LLCallingCardBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid ) : - LLItemBridge(inventory, /*root,*/ uuid) + LLItemBridge(inventory, root, uuid) { mObserver = new LLCallingCardObserver(this); LLAvatarTracker::instance().addObserver(mObserver); @@ -3366,7 +3882,8 @@ LLCallingCardBridge::~LLCallingCardBridge() void LLCallingCardBridge::refreshFolderViewItem() { - LLFolderViewItem* itemp = mInventoryPanel ? mInventoryPanel->getRootFolder()->getItemByID(mUUID) : NULL; + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; if (itemp) { itemp->refresh(); @@ -3374,7 +3891,7 @@ void LLCallingCardBridge::refreshFolderViewItem() } // virtual -void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string action) { if ("begin_im" == action) { @@ -3395,7 +3912,7 @@ void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* handle_lure(item->getCreatorUUID()); } } - else LLItemBridge::performAction(folder, model, action); + else LLItemBridge::performAction(model, action); } LLUIImagePtr LLCallingCardBridge::getIcon() const @@ -3425,12 +3942,18 @@ std::string LLCallingCardBridge::getLabelSuffix() const void LLCallingCardBridge::openItem() { LLViewerInventoryItem* item = getItem(); - if(item && !item->getCreatorUUID().isNull()) + + if (item) { - BOOL online; - online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); - LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } +/* + LLViewerInventoryItem* item = getItem(); + if(item && !item->getCreatorUUID().isNull()) + { + LLAvatarActions::showProfile(item->getCreatorUUID()); + } +*/ } void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) @@ -3489,16 +4012,17 @@ BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, // check the type switch(cargo_type) { - case DAD_TEXTURE: - case DAD_SOUND: - case DAD_LANDMARK: - case DAD_SCRIPT: - case DAD_CLOTHING: - case DAD_OBJECT: - case DAD_NOTECARD: - case DAD_BODYPART: - case DAD_ANIMATION: - case DAD_GESTURE: + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: { LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; const LLPermissions& perm = inv_item->getPermissions(); @@ -3614,7 +4138,7 @@ void LLNotecardBridge::openItem() LLViewerInventoryItem* item = getItem(); if (item) { - open_notecard(item, getPrefix() + item->getName(), LLUUID::null, FALSE); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } } @@ -3645,7 +4169,9 @@ std::string LLGestureBridge::getLabelSuffix() const { if( LLGestureMgr::instance().isGestureActive(mUUID) ) { - return LLItemBridge::getLabelSuffix() + " (active)"; + LLStringUtil::format_map_t args; + args["[GESLABEL]"] = LLItemBridge::getLabelSuffix(); + return LLTrans::getString("ActiveGesture", args); } else { @@ -3654,9 +4180,9 @@ std::string LLGestureBridge::getLabelSuffix() const } // virtual -void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLGestureBridge::performAction(LLInventoryModel* model, std::string action) { - if ("activate" == action) + if (isAddAction(action)) { LLGestureMgr::instance().activateGesture(mUUID); @@ -3668,7 +4194,7 @@ void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* mode gInventory.updateItem(item); gInventory.notifyObservers(); } - else if ("deactivate" == action) + else if (isRemoveAction(action)) { LLGestureMgr::instance().deactivateGesture(mUUID); @@ -3680,28 +4206,45 @@ void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* mode gInventory.updateItem(item); gInventory.notifyObservers(); } - else LLItemBridge::performAction(folder, model, action); + else if("play" == action) + { + if(!LLGestureMgr::instance().isGestureActive(mUUID)) + { + // we need to inform server about gesture activating to be consistent with LLPreviewGesture and LLGestureComboList. + BOOL inform_server = TRUE; + BOOL deactivate_similar = FALSE; + LLGestureMgr::instance().setGestureLoadedCallback(mUUID, boost::bind(&LLGestureBridge::playGesture, mUUID)); + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + llassert(item); + if (item) + { + LLGestureMgr::instance().activateGestureWithAsset(mUUID, item->getAssetUUID(), inform_server, deactivate_similar); + } + } + else + { + playGesture(mUUID); + } + } + else LLItemBridge::performAction(model, action); } void LLGestureBridge::openItem() { LLViewerInventoryItem* item = getItem(); - if (!item) return; - // See if we can bring an existing preview to the front - if(!LLPreview::show(mUUID)) + if (item) { - LLUUID item_id = mUUID; - std::string title = getPrefix() + item->getName(); - LLUUID object_id = LLUUID::null; - - // TODO: save the rectangle - LLPreviewGesture* preview = LLPreviewGesture::show(title, item_id, object_id); - preview->setFocus(TRUE); - - // Force to be entirely onscreen. - gFloaterView->adjustToFitScreen(preview, FALSE); + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } +/* + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null); + preview->setFocus(TRUE); + } +*/ } BOOL LLGestureBridge::removeItem() @@ -3758,6 +4301,20 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } +// static +void LLGestureBridge::playGesture(const LLUUID& item_id) +{ + if (LLGestureMgr::instance().isGesturePlaying(item_id)) + { + LLGestureMgr::instance().stopGesture(item_id); + } + else + { + LLGestureMgr::instance().playGesture(item_id); + } +} + + // +=================================================+ // | LLAnimationBridge | // +=================================================+ @@ -3790,20 +4347,19 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } // virtual -void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLAnimationBridge::performAction(LLInventoryModel* model, std::string action) { if ((action == "playworld") || (action == "playlocal")) { // See if we can bring an existing preview to the front if(LLPreview::show( mUUID )) return; - - LLViewerInventoryItem *item = getItem(); - if( item ) + + if( getItem() ) { - S32 activate = 0; - if ("playworld" == action) activate = 1; - if ("playlocal" == action) activate = 2; + LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE; + if ("playworld" == action) activate = LLPreviewAnim::PLAY; + if ("playlocal" == action) activate = LLPreviewAnim::AUDITION; S32 left, top; gFloaterView->getNewFloaterPosition(&left, &top); @@ -3811,7 +4367,7 @@ void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* mo rect.translate( left - rect.mLeft, top - rect.mTop ); LLPreviewAnim* preview = new LLPreviewAnim("preview anim", rect, - getPrefix() + item->getName(), + "Animations: " + getItem()->getName(), mUUID, activate); // Force to be entirely onscreen. @@ -3820,33 +4376,26 @@ void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* mo } else { - LLItemBridge::performAction(folder, model, action); + LLItemBridge::performAction(model, action); } } void LLAnimationBridge::openItem() { - // See if we can bring an existing preview to the front - if(LLPreview::show( mUUID )) - return; - - LLViewerInventoryItem* item = getItem(); - if( item ) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewAnimRect"); - rect.translate( left - rect.mLeft, top - rect.mTop ); - LLPreviewAnim* preview = new LLPreviewAnim("preview anim", - rect, - getPrefix() + item->getName(), - mUUID, - 0); - preview->setFocus(TRUE); - // Force to be entirely onscreen. - gFloaterView->adjustToFitScreen(preview, FALSE); - } + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } +/* + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); + } +*/ +} // +=================================================+ // | LLObjectBridge | @@ -3856,42 +4405,17 @@ void LLAnimationBridge::openItem() LLUUID LLObjectBridge::sContextMenuItemID; LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, + LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) : - LLItemBridge(inventory, uuid) + LLItemBridge(inventory, root, uuid) { mAttachPt = (flags & 0xff); // low bye of inventory flags mIsMultiObject = ( flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE; mInvType = type; } -BOOL LLObjectBridge::isItemRemovable() -{ - LLInventoryModel* model = mInventoryPanel->getModel(); - if (!model) - { - return FALSE; - } -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g - if ( (rlv_handler_t::isEnabled()) && (RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) && (!RlvFolderLocks::instance().canRemoveItem(mUUID)) ) - { - return FALSE; - } -// [/RLVa:KB] - const LLInventoryObject *obj = model->getItem(mUUID); - if (obj && obj->getIsLinkType()) - { - return TRUE; - } - // - //LLVOAvatar* avatar = gAgentAvatarp; - //if(!avatar) return FALSE; - //if(avatar->isWearingAttachment(mUUID)) return FALSE; - // - return LLInvFVBridge::isItemRemovable(); -} - LLUIImagePtr LLObjectBridge::getIcon() const { return LLInventoryIcon::getIcon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject ); @@ -3909,22 +4433,21 @@ LLInventoryObject* LLObjectBridge::getObject() const } // virtual -void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) { - if ("attach" == action || "wear_add" == action) + if (isAddAction(action)) { - bool replace = ("attach" == action); // Replace if "Wear"ing. LLUUID object_id = mUUID; LLViewerInventoryItem* item; item = (LLViewerInventoryItem*)gInventory.getItem(object_id); if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID())) { - rez_attachment(item, NULL, replace); + rez_attachment(item, NULL, true); } - else if(item && item->isComplete()) + else if(item && item->isFinished()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new RezAttachmentCallback(0, replace); + LLPointer cb = new RezAttachmentCallback(0); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), @@ -3935,12 +4458,16 @@ void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model } gFocusMgr.setKeyboardFocus(NULL); } - else if ("detach" == action) + else if ("wear_add" == action) + { + LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. + } + else if (isRemoveAction(action)) { LLInventoryItem* item = gInventory.getItem(mUUID); if(item) { - LLVOAvatar::detachAttachmentIntoInventory(item->getLinkedUUID()); + LLVOAvatarSelf::detachAttachmentIntoInventory(item->getLinkedUUID()); } } else if ("edit" == action) @@ -3992,84 +4519,63 @@ void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model // Could be first use LLFirstUse::useBuild(); } - else LLItemBridge::performAction(folder, model, action); + else LLItemBridge::performAction(model, action); } void LLObjectBridge::openItem() { - LLVOAvatar* avatar = gAgentAvatarp; - if (!avatar) - { - return; - } - if (avatar->isWearingAttachment(mUUID)) - { -// [RLVa:KB] - if ( !(rlv_handler_t::isEnabled()) || (gRlvAttachmentLocks.canDetach(getItem()))) - performAction(NULL, NULL, "detach"); -// [/RLVa:KB] - } - else - { - performAction(NULL, NULL, "attach"); - } + // object double-click action is to wear/unwear object + performAction(getInventoryModel(), + get_is_item_worn(mUUID) ? "detach" : "attach"); } std::string LLObjectBridge::getLabelSuffix() const { - LLVOAvatar* avatar = gAgentAvatarp; - if( avatar && avatar->isWearingAttachment( mUUID ) ) + if (get_is_item_worn(mUUID)) { - std::string attachment_point_name = avatar->getAttachedPointName(mUUID); - LLStringUtil::toLower(attachment_point_name); - LLViewerObject* pObj = (rlv_handler_t::isEnabled()) ? avatar->getWornAttachment( mUUID ) : NULL; -// [RLVa:KB] - if ( pObj && (gRlvAttachmentLocks.isLockedAttachment(pObj) || - gRlvAttachmentLocks.isLockedAttachmentPoint(RlvAttachPtLookup::getAttachPointIndex(pObj),RLV_LOCK_REMOVE))) + if (!isAgentAvatarValid()) // Error condition, can't figure out attach point { - return LLItemBridge::getLabelSuffix() + std::string(" (locked to ") + attachment_point_name + std::string(")"); + return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn"); } -// [/RLVa:KB] - return LLItemBridge::getLabelSuffix() + std::string(" (worn on ") + attachment_point_name + std::string(")"); - } - else - { - // testzone attachpt - if(avatar) + std::string attachment_point_name = gAgentAvatarp->getAttachedPointName(mUUID); + if (attachment_point_name == LLStringUtil::null) // Error condition, invalid attach point { - std::map >::iterator iter = avatar->mUnsupportedAttachmentPoints.begin(); - std::map >::iterator end = avatar->mUnsupportedAttachmentPoints.end(); + attachment_point_name = "Invalid Attachment"; + std::map >::iterator iter = gAgentAvatarp->mUnsupportedAttachmentPoints.begin(); + std::map >::iterator end = gAgentAvatarp->mUnsupportedAttachmentPoints.end(); for( ; iter != end; ++iter) + { if((*iter).second.first == mUUID) { -// [RLVa:KB] - LLViewerObject* pObj = (rlv_handler_t::isEnabled()) ? gObjectList.findObject((*iter).second.second) : NULL; - if ( pObj && (gRlvAttachmentLocks.isLockedAttachment(pObj) || - gRlvAttachmentLocks.isLockedAttachmentPoint(RlvAttachPtLookup::getAttachPointIndex(pObj),RLV_LOCK_REMOVE))) - { - return LLItemBridge::getLabelSuffix() + std::string(" (locked to unsupported point %d)", (*iter).first); - } -// [/RLVa:KB] - return LLItemBridge::getLabelSuffix() + llformat(" (worn on unsupported point %d)", (*iter).first); + attachment_point_name = llformat("unsupported point %d)", (*iter).first); } + } } - // - return LLItemBridge::getLabelSuffix(); + // e.g. "(worn on ...)" / "(attached to ...)" + LLStringUtil::format_map_t args; + args["[ATTACHMENT_POINT]"] = attachment_point_name; + + return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args); } + return LLItemBridge::getLabelSuffix(); } void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace) { -// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1a) | Added: RLVa-1.2.1a - // If no attachment point was specified, try looking it up from the item name - if ( (rlv_handler_t::isEnabled()) && (!attachment) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) + const LLUUID& item_id = item->getLinkedUUID(); + + // Check for duplicate request. + if (isAgentAvatarValid() && + (gAgentAvatarp->attachmentWasRequested(item_id) || + gAgentAvatarp->isWearingAttachment(item_id))) { - attachment = RlvAttachPtLookup::getAttachPoint(item); + llwarns << "duplicate attachment request, ignoring" << llendl; + return; } -// [/RLVa:KB] + gAgentAvatarp->addAttachmentRequest(item_id); S32 attach_pt = 0; - if (gAgentAvatarp && attachment) + if (isAgentAvatarValid() && attachment) { for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter) @@ -4083,31 +4589,22 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach } LLSD payload; - payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link. + payload["item_id"] = item_id; // Wear the base object in case this is a link. payload["attachment_point"] = attach_pt; payload["is_add"] = !replace; - if (replace && attachment && attachment->getNumObjects() > 0) + if (replace && + (attachment && attachment->getNumObjects() > 0)) { -// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1a) | Modified: RLVa-1.2.1a - // Block if we can't "replace wear" what's currently there - if ( (rlv_handler_t::isEnabled()) && ((gRlvAttachmentLocks.canAttach(attachment) & RLV_WEAR_REPLACE) == 0) ) - return; -// [/RLVa:KB] - LLNotificationsUtil::add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez); + LLNotificationsUtil::add("ReplaceAttachment", LLSD(), payload, confirm_attachment_rez); } else { -// [RLVa:KB] - Checked: 2010-08-07 (RLVa-1.2.0i) | Modified: RLVa-1.2.0i - // Block wearing anything on a non-attachable attachment point - if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.isLockedAttachmentPoint(attach_pt, RLV_LOCK_ADD)) ) - return; -// [/RLVa:KB] LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/); } } -bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response) +bool confirm_attachment_rez(const LLSD& notification, const LLSD& response) { if (!gAgentAvatarp->canAttachMoreObjects()) { @@ -4125,22 +4622,44 @@ bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& respon if (itemp) { + /* + { + U8 attachment_pt = notification["payload"]["attachment_point"].asInteger(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID()); + msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner()); + msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt); + pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions()); + msg->addStringFast(_PREHASH_Name, itemp->getName()); + msg->addStringFast(_PREHASH_Description, itemp->getDescription()); + msg->sendReliable(gAgent.getRegion()->getHost()); + return false; + } + */ + // Queue up attachments to be sent in next idle tick, this way the // attachments are batched up all into one message versus each attachment // being sent in its own separate attachments message. U8 attachment_pt = notification["payload"]["attachment_point"].asInteger(); BOOL is_add = notification["payload"]["is_add"].asBoolean(); - LLAttachmentsMgr::instance().addAttachment(item_id, attachment_pt, is_add); + LLAttachmentsMgr::instance().addAttachment(item_id, + attachment_pt, + is_add); } } return false; } -LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_replace_attachment_rez); +static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_attachment_rez); void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - // *TODO: Translate menuentry_vec_t items; menuentry_vec_t disabled_items; if(isItemInTrash()) @@ -4159,16 +4678,11 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) LLInventoryItem* item = getItem(); if(item) { - LLVOAvatar *avatarp = gAgentAvatarp; - if( !avatarp ) - { - return; - } - + if (!isAgentAvatarValid()) return; if( get_is_item_worn( mUUID ) ) { - items.push_back(std::string("Attach Separator")); + items.push_back(std::string("Wearable And Object Separator")); items.push_back(std::string("Detach From Yourself")); items.push_back(std::string("Wearable Edit")); // [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a | OK @@ -4178,35 +4692,30 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } else // testzone attachpt - if( avatarp->isWearingUnsupportedAttachment( mUUID ) ) + if( gAgentAvatarp->isWearingUnsupportedAttachment( mUUID ) ) { items.push_back(std::string("Detach From Yourself")); } - else // - if( !isItemInTrash() ) + else if (!isItemInTrash() && !isLinkedObjectInTrash() && !isLinkedObjectMissing() && !isCOFFolder()) { - items.push_back(std::string("Attach Separator")); - items.push_back(std::string("Object Wear")); - if (gHippoGridManager->getConnectedGrid()->supportsInvLinks()) - items.push_back(std::string("Object Add")); - if (!avatarp->canAttachMoreObjects()) - { - disabled_items.push_back(std::string("Object Add")); - } + items.push_back(std::string("Wearable And Object Separator")); + items.push_back(std::string("Wearable And Object Wear")); + if (InventoryLinksEnabled()) + items.push_back(std::string("Wearable Add")); items.push_back(std::string("Attach To")); items.push_back(std::string("Attach To HUD")); // commented out for DEV-32347 - AND Commented back in for non-morons. -HgB items.push_back(std::string("Restore to Last Position")); - if (!avatarp->canAttachMoreObjects()) + if (!gAgentAvatarp->canAttachMoreObjects()) { - disabled_items.push_back(std::string("Object Wear")); - disabled_items.push_back(std::string("Object Add")); + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); disabled_items.push_back(std::string("Attach To")); disabled_items.push_back(std::string("Attach To HUD")); } -// [RLVa:KB] - Checked: 2010-09-03 (RLVa-1.2.1a) | Modified: RLVa-1.2.1a | OK +/*// [RLVa:KB] - Checked: 2010-09-03 (RLVa-1.2.1a) | Modified: RLVa-1.2.1a | OK else if (rlv_handler_t::isEnabled()) { ERlvWearMask eWearMask = gRlvAttachmentLocks.canAttach(item); @@ -4215,7 +4724,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if ((eWearMask & RLV_WEAR_ADD) == 0) disabled_items.push_back(std::string("Object Add")); } -// [/RLVa:KB] +// [/RLVa:KB]*/ LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE); LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE); @@ -4223,10 +4732,10 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) && (attach_menu->getChildCount() == 0) && attach_hud_menu && (attach_hud_menu->getChildCount() == 0) - && avatarp) + && isAgentAvatarValid()) { - for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); - iter != avatarp->mAttachmentPoints.end(); ) + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; @@ -4234,7 +4743,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if (attachment->getIsHUDAttachment()) { // [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.0.0c) - attach_hud_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(), + attach_hud_menu->addChild(new_item = new LLMenuItemCallGL(attachment->getName(), NULL, //&LLObjectBridge::attachToAvatar, (rlv_handler_t::isEnabled()) ? &rlvAttachToEnabler : NULL, &attach_label, (void*)attachment)); @@ -4243,14 +4752,14 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { // [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.0.0c) - attach_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(), + attach_menu->addChild(new_item = new LLMenuItemCallGL(attachment->getName(), NULL, //&LLObjectBridge::attachToAvatar, (rlv_handler_t::isEnabled()) ? &rlvAttachToEnabler : NULL, &attach_label, (void*)attachment)); // [/RLVa:KB] } - LLSimpleListener* callback = mInventoryPanel->getListenerByName("Inventory.AttachObject"); + LLSimpleListener* callback = mInventoryPanel.get()->getListenerByName("Inventory.AttachObject"); if (callback) { @@ -4258,8 +4767,8 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } } LLMenuItemCallGL *new_item = new LLMenuItemCallGL("Custom...", NULL, NULL); - attach_menu->append(new_item); - LLSimpleListener* callback = mInventoryPanel->getListenerByName("Inventory.AttachCustom"); + attach_menu->addChild(new_item); + LLSimpleListener* callback = mInventoryPanel.get()->getListenerByName("Inventory.AttachCustom"); new_item->addListener(callback, "on_click", LLSD()); } } @@ -4272,7 +4781,7 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { if(!isItemRenameable()) return FALSE; - LLPreview::rename(mUUID, getPrefix() + new_name); + LLPreview::rename(mUUID, std::string("Object: ") + new_name); LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; @@ -4284,6 +4793,7 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); + model->notifyObservers(); if (isAgentAvatarValid()) @@ -4309,34 +4819,11 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) void LLLSLTextBridge::openItem() { -// [RLVa:KB] - Checked: 2009-11-11 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a - if (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWSCRIPT)) - { - RlvNotifications::notifyBlockedViewScript(); - return; - } -// [/RLVa:KB] + LLViewerInventoryItem* item = getItem(); - // See if we can bring an exiting preview to the front - if(!LLPreview::show(mUUID)) + if (item) { - LLViewerInventoryItem* item = getItem(); - if (item) - { - // There isn't one, so make a new preview - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewScriptRect"); - rect.translate(left - rect.mLeft, top - rect.mTop); - - LLPreviewLSL* preview = new LLPreviewLSL("preview lsl text", - rect, - getPrefix() + item->getName(), - mUUID); - preview->setFocus(TRUE); - // keep onscreen - gFloaterView->adjustToFitScreen(preview, FALSE); - } + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } } @@ -4345,655 +4832,29 @@ void LLLSLTextBridge::openItem() // +=================================================+ LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid, LLAssetType::EType asset_type, LLInventoryType::EType inv_type, - LLWearableType::EType wearable_type) : - LLItemBridge(inventory, /*root,*/ uuid), + LLWearableType::EType wearable_type) : + LLItemBridge(inventory, root, uuid), mAssetType( asset_type ), mWearableType(wearable_type) { mInvType = inv_type; } -// *NOTE: hack to get from avatar inventory to avatar -void wear_inventory_item_on_avatar( LLInventoryItem* item ) -{ - if(item) - { - lldebugs << "wear_inventory_item_on_avatar( " << item->getName() - << " )" << llendl; - - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - LLWearableBridge::onWearOnAvatarArrived, - new LLUUID(item->getLinkedUUID())); - } -} - -// [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i -// Moved to llinventorybridge.h because we need it in RlvForceWearLegacy -/* -struct LLFoundData -{ - LLFoundData(const LLUUID& item_id, - const LLUUID& asset_id, - const std::string& name, - LLAssetType::EType asset_type) : - mItemID(item_id), - mAssetID(asset_id), - mName(name), - mAssetType(asset_type), - mWearable( NULL ) {} - - LLUUID mItemID; - LLUUID mAssetID; - std::string mName; - LLAssetType::EType mAssetType; - LLWearable* mWearable; -}; - -struct LLWearableHoldingPattern -{ - LLWearableHoldingPattern() : mResolved(0) {} - ~LLWearableHoldingPattern() - { - for_each(mFoundList.begin(), mFoundList.end(), DeletePointer()); - mFoundList.clear(); - } - typedef std::list found_list_t; - found_list_t mFoundList; - S32 mResolved; -}; - -*/ -// [/RLVa:KB] - -class LLOutfitObserver : public LLInventoryFetchItemsObserver -{ -public: - LLOutfitObserver(const uuid_vec_t& ids, const LLUUID& cat_id, bool copy_items, bool append) : - LLInventoryFetchItemsObserver(ids), - mCatID(cat_id), - mCopyItems(copy_items), - mAppend(append) - {} - ~LLOutfitObserver() {} - virtual void done(); //public - -protected: - LLUUID mCatID; - bool mCopyItems; - bool mAppend; -}; - - -class LLWearInventoryCategoryCallback : public LLInventoryCallback -{ -public: - LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append) - { - mCatID = cat_id; - mAppend = append; - } - void fire(const LLUUID& item_id) - { - /* - * Do nothing. We only care about the destructor - * - * The reason for this is that this callback is used in a hack where the - * same callback is given to dozens of items, and the destructor is called - * after the last item has fired the event and dereferenced it -- if all - * the events actually fire! - */ - } - -protected: - ~LLWearInventoryCategoryCallback() - { - // Is the destructor called by ordinary dereference, or because the app's shutting down? - // If the inventory callback manager goes away, we're shutting down, no longer want the callback. - if( LLInventoryCallbackManager::is_instantiated() ) - { - wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend); - } - else - { - llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl; - } - } - -private: - LLUUID mCatID; - bool mAppend; -}; - -void LLOutfitObserver::done() -{ - // We now have an outfit ready to be copied to agent inventory. Do - // it, and wear that outfit normally. - if(mCopyItems) - { - LLInventoryCategory* cat = gInventory.getCategory(mCatID); - std::string name; - if(!cat) - { - // should never happen. - name = "New Outfit"; - } - else - { - name = cat->getName(); - } - LLViewerInventoryItem* item = NULL; - uuid_vec_t::iterator it = mComplete.begin(); - uuid_vec_t::iterator end = mComplete.end(); - LLUUID pid; - for(; it < end; ++it) - { - item = (LLViewerInventoryItem*)gInventory.getItem(*it); - if(item) - { - if(LLInventoryType::IT_GESTURE == item->getInventoryType()) - { - pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); - } - else - { - pid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); - } - break; - } - } - if(pid.isNull()) - { - pid = gInventory.getRootFolderID(); - } - - LLUUID cat_id = gInventory.createNewCategory( - pid, - LLFolderType::FT_NONE, - name); - mCatID = cat_id; - LLPointer cb = new LLWearInventoryCategoryCallback(mCatID, mAppend); - it = mComplete.begin(); - for(; it < end; ++it) - { - item = (LLViewerInventoryItem*)gInventory.getItem(*it); - if(item) - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - cat_id, - std::string(), - cb); - } - } - } - else - { - // Wear the inventory category. - wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend); - } -} - -class LLOutfitFetch : public LLInventoryFetchDescendentsObserver -{ -public: - LLOutfitFetch(const LLUUID& id, bool copy_items, bool append) : - LLInventoryFetchDescendentsObserver(id), - mCopyItems(copy_items), - mAppend(append) {} - ~LLOutfitFetch() {} - virtual void done(); -protected: - bool mCopyItems; - bool mAppend; -}; - -void LLOutfitFetch::done() -{ - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - - // Avoid passing a NULL-ref as mCompleteFolders.front() down to - // gInventory.collectDescendents() - if( mComplete.empty() ) - { - llwarns << "LLOutfitFetch::done with empty mCompleteFolders" << llendl; - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - return; - } - - gInventory.collectDescendents(mComplete.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - S32 count = item_array.count(); - if(!count) - { - llwarns << "Nothing fetched in category " << mComplete.front() - << llendl; - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - return; - } - - uuid_vec_t ids; - for(S32 i = 0; i < count; ++i) - { - ids.push_back(item_array.get(i)->getUUID()); - } - - LLOutfitObserver* outfit = new LLOutfitObserver(ids, mComplete.front(), mCopyItems, mAppend); - - - // clean up, and remove this as an observer since the call to the - // outfit could notify observers and throw us into an infinite - // loop. - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - - // increment busy count and either tell the inventory to check & - // call done, or add this object to the inventory for observation. - inc_busy_count(); - - // do the fetch - outfit->startFetch(); - if(outfit->isFinished()) - { - // everything is already here - call done. - outfit->done(); - } - else - { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. - gInventory.addObserver(outfit); - } -} - -void wear_outfit_by_name(const std::string& name) -{ - llinfos << "Wearing category " << name << llendl; - inc_busy_count(); - - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLNameCategoryCollector has_name(name); - gInventory.collectDescendentsIf(gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - has_name); - bool copy_items = false; - LLInventoryCategory* cat = NULL; - if (cat_array.count() > 0) - { - // Just wear the first one that matches - cat = cat_array.get(0); - } - else - { - gInventory.collectDescendentsIf(LLUUID::null, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - has_name); - if(cat_array.count() > 0) - { - cat = cat_array.get(0); - copy_items = true; - } - } - - if(cat) - { - wear_inventory_category(cat, copy_items, false); - } - else - { - llwarns << "Couldn't find outfit " <getName() - << " )" << llendl; - // What we do here is get the complete information on the items in - // the inventory, and set up an observer that will wait for that to - // happen. - LLOutfitFetch* outfit = new LLOutfitFetch(category->getUUID(), copy, append); - outfit->startFetch(); - inc_busy_count(); - if(outfit->isFinished()) - { - // everything is already here - call done. - outfit->done(); - } - else - { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. - gInventory.addObserver(outfit); - } -} - -// *NOTE: hack to get from avatar inventory to avatar -void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append, BOOL replace) -{ - // Avoid unintentionally overwriting old wearables. We have to do - // this up front to avoid having to deal with the case of multiple - // wearables being dirty. - if(!category) return; - lldebugs << "wear_inventory_category_on_avatar( " << category->getName() - << " )" << llendl; - - LLWearInfo* userdata = new LLWearInfo; - userdata->mAppend = append; - userdata->mReplace = replace; - userdata->mCategoryID = category->getUUID(); - - if( gFloaterCustomize ) - { - gFloaterCustomize->askToSaveIfDirty( - wear_inventory_category_on_avatar_step2, - userdata); - } - else - { - wear_inventory_category_on_avatar_step2( - TRUE, - userdata ); - } -} - - -void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata ) -{ - LLWearInfo* wear_info = (LLWearInfo*)userdata; - if (!wear_info) return; - - // Find all the wearables that are in the category's subtree. - lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl; - if(proceed) - { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLFindWearables is_wearable; - gInventory.collectDescendentsIf(wear_info->mCategoryID, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_wearable); - S32 i; - S32 wearable_count = item_array.count(); - - LLInventoryModel::item_array_t obj_items_new; - LLCOFMgr::getDescendentsOfAssetType(wear_info->mCategoryID, obj_items_new, LLAssetType::AT_OBJECT, false); - S32 obj_count = obj_items_new.count(); - - // Find all gestures in this folder - LLInventoryModel::cat_array_t gest_cat_array; - LLInventoryModel::item_array_t gest_item_array; - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - gInventory.collectDescendentsIf(wear_info->mCategoryID, - gest_cat_array, - gest_item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_gesture); - S32 gest_count = gest_item_array.count(); - - if( !wearable_count && !obj_count && !gest_count) - { - LLNotificationsUtil::add("CouldNotPutOnOutfit"); - delete wear_info; - return; - } - - // Processes that take time should show the busy cursor - if (wearable_count > 0 || obj_count > 0) - { - inc_busy_count(); - } - - // Activate all gestures in this folder - if (gest_count > 0) - { - llinfos << "Activating " << gest_count << " gestures" << llendl; - - LLGestureMgr::instance().activateGestures(gest_item_array); - - // Update the inventory item labels to reflect the fact - // they are active. - LLViewerInventoryCategory* catp = gInventory.getCategory(wear_info->mCategoryID); - if (catp) - { - gInventory.updateCategory(catp); - gInventory.notifyObservers(); - } - } - - if(wearable_count > 0) - { - // Note: can't do normal iteration, because if all the - // wearables can be resolved immediately, then the - // callback will be called (and this object deleted) - // before the final getNextData(). -// LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; -// [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i - LLWearableHoldingPattern* holder = new LLWearableHoldingPattern(wear_info->mAppend); -// [/RLVa:KB] - LLFoundData* found; - LLDynamicArray found_container; - for(i = 0; i < wearable_count; ++i) - { - found = new LLFoundData(item_array.get(i)->getLinkedUUID(), - item_array.get(i)->getAssetUUID(), - item_array.get(i)->getName(), - item_array.get(i)->getType()); - holder->mFoundList.push_front(found); - found_container.put(found); - } - for(i = 0; i < wearable_count; ++i) - { -// [RLVa:KB] - Part of LLWearableHoldingPattern -// gAddToOutfit = wear_info->mAppend; -// [/RLVa:KB] - - found = found_container.get(i); - LLWearableList::instance().getAsset(found->mAssetID, - found->mName, - found->mAssetType, - wear_inventory_category_on_avatar_loop, - (void*)holder); - } - } - - const LLUUID idCOF = LLCOFMgr::instance().getCOF(); - - // - // - Attachments: include COF contents only if appending. - // - if (!wear_info->mReplace) - { - LLInventoryModel::item_array_t obj_items; - if (wear_info->mAppend) - LLCOFMgr::getDescendentsOfAssetType(idCOF, obj_items, LLAssetType::AT_OBJECT, false); -// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b - else if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) - { - // Make sure that all currently locked attachments remain in COF when replacing - LLCOFMgr::getDescendentsOfAssetType(idCOF, obj_items, LLAssetType::AT_OBJECT, false); - obj_items.erase(std::remove_if(obj_items.begin(), obj_items.end(), rlvPredCanRemoveItem), obj_items.end()); - } -// [/RLVa:KB] -// getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false); -// [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b - // Filter out any new attachments that can't be worn before adding them - if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) - obj_items_new.erase(std::remove_if(obj_items_new.begin(), obj_items_new.end(), - RlvPredCanNotWearItem(RLV_WEAR_ADD)), obj_items_new.end()); - for (S32 idxObjNew = 0; idxObjNew < obj_items_new.count(); idxObjNew++) - RlvAttachmentLockWatchdog::instance().onWearAttachment(obj_items_new.get(idxObjNew).get() , RLV_WEAR_ADD); - obj_items.insert(obj_items.end(), obj_items_new.begin(), obj_items_new.end()); -// [/RLVa:KB] - - LLAgentWearables::userUpdateAttachments(obj_items); - } - else - { - for (S32 idxItem = 0, cntItem = obj_items_new.count(); idxItem < cntItem; idxItem++) - { - LLInventoryItem* pItem = obj_items_new.get(idxItem); - -// [RLVa:KB] - Checked: 2010-11-21 (RLVa-1.1.3c) | Added: RLVa-1.1.3c - if ( (rlv_handler_t::isEnabled()) && (!(RLV_WEAR_REPLACE & gRlvAttachmentLocks.canAttach(pItem))) ) - continue; -// [/RLVa:KB] - - LLAttachmentsMgr::instance().addAttachment(pItem->getLinkedUUID(), 0, false); - } - } - - if (!wear_info->mAppend) - LLCOFMgr::instance().addBOFLink(wear_info->mCategoryID); - } - delete wear_info; - wear_info = NULL; -} - -void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void* data) -{ - LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data; -// [RLVa:KB] - Part of LLWearableHoldingPattern -// BOOL append= gAddToOutfit; -// [/RLVa:KB] - - - - if(wearable) - { - for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin(); - iter != holder->mFoundList.end(); ++iter) - { - LLFoundData* data = *iter; - if(wearable->getAssetID() == data->mAssetID) - { - data->mWearable = wearable; - break; - } - } - } - holder->mResolved += 1; - if(holder->mResolved >= (S32)holder->mFoundList.size()) - { -// wear_inventory_category_on_avatar_step3(holder, append); -// [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i - wear_inventory_category_on_avatar_step3(holder, holder->mAddToOutfit); -// [/RLVa:KB] - } -} - - - - -void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append) -{ - lldebugs << "wear_inventory_category_on_avatar_step3()" << llendl; - LLInventoryItem::item_array_t items; - LLDynamicArray< LLWearable* > wearables; - - // For each wearable type, find the first instance in the category - // that we recursed through. - for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) - { - for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin(); - iter != holder->mFoundList.end(); ++iter) - { - LLFoundData* data = *iter; - LLWearable* wearable = data->mWearable; - if( wearable && ((S32)wearable->getType() == i) ) - { - LLViewerInventoryItem* item; - item = (LLViewerInventoryItem*)gInventory.getLinkedItem(data->mItemID); - if( item && (item->getAssetUUID() == wearable->getAssetID()) ) - { - //RN: after discussing with Brashears, I disabled this code - //Metadata should reside in the item, not the asset - //And this code does not handle failed asset uploads properly -// if(!wearable->isMatchedToInventoryItem(item )) -// { -// wearable = LLWearableList::instance().createWearableMatchedToInventoryItem( wearable, item ); -// // Now that we have an asset that matches the -// // item, update the item to point to the new -// // asset. -// item->setAssetUUID(wearable->getID()); -// item->updateAssetOnServer(); -// } -// [RLVa:KB] - Checked: 2010-09-28 (RLVa-1.1.3b) | Modified: RLVa-1.1.4a - if (!gRlvWearableLocks.canWear(item)) - { - continue; - } -// [/RLVa:KB] - items.put(item); - wearables.put(wearable); - } - break; - } - } - } - - if(wearables.count() > 0) - { - gAgentWearables.setWearableOutfit(items, wearables, !append); - gInventory.notifyObservers(); - } - - delete holder; - - dec_busy_count(); -} - void remove_inventory_category_from_avatar( LLInventoryCategory* category ) { if(!category) return; lldebugs << "remove_inventory_category_from_avatar( " << category->getName() << " )" << llendl; - - - LLUUID* uuid = new LLUUID(category->getUUID()); - - if( gFloaterCustomize ) + if (gAgentCamera.cameraCustomizeAvatar()) { - gFloaterCustomize->askToSaveIfDirty( - remove_inventory_category_from_avatar_step2, - uuid); + gFloaterCustomize->askToSaveIfDirty(boost::bind(&remove_inventory_category_from_avatar_step2,_1,category->getUUID())); } else - { - remove_inventory_category_from_avatar_step2( - TRUE, - uuid ); - } + remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() ); } struct OnRemoveStruct @@ -5005,19 +4866,17 @@ struct OnRemoveStruct } }; -void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) +void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id) { // Find all the wearables that are in the category's subtree. - LLUUID* category_id = (LLUUID *)userdata; - lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl; if(proceed) { LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; LLFindWearables is_wearable; - gInventory.collectDescendentsIf(*category_id, + gInventory.collectDescendentsIf(category_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, @@ -5028,7 +4887,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) LLInventoryModel::cat_array_t obj_cat_array; LLInventoryModel::item_array_t obj_item_array; LLIsType is_object( LLAssetType::AT_OBJECT ); - gInventory.collectDescendentsIf(*category_id, + gInventory.collectDescendentsIf(category_id, obj_cat_array, obj_item_array, LLInventoryModel::EXCLUDE_TRASH, @@ -5039,7 +4898,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) LLInventoryModel::cat_array_t gest_cat_array; LLInventoryModel::item_array_t gest_item_array; LLIsType is_gesture( LLAssetType::AT_GESTURE ); - gInventory.collectDescendentsIf(*category_id, + gInventory.collectDescendentsIf(category_id, gest_cat_array, gest_item_array, LLInventoryModel::EXCLUDE_TRASH, @@ -5051,13 +4910,14 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) for(i = 0; i < wearable_count; ++i) { LLViewerInventoryItem *item = item_array.get(i); -// if( gAgentWearables.isWearingItem (item_array.get(i)->getUUID()) ) -// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.1.3b) | Modified: RLVa-0.2.2a - LLWearable* pWearable = gAgentWearables.getWearableFromItemID(item_array.get(i)->getLinkedUUID()); - if ( (pWearable) && ( (!rlv_handler_t::isEnabled()) || (gRlvWearableLocks.canRemove(pWearable->getType())) ) ) -// [/RLVa:KB] + if (item->getType() == LLAssetType::AT_BODYPART) + continue; + if (gAgent.isTeen() && item->isWearableType() && + (item->getWearableType() == LLWearableType::WT_UNDERPANTS || item->getWearableType() == LLWearableType::WT_UNDERSHIRT)) + continue; + if (get_is_item_worn(item->getUUID())) { - LLWearableList::instance().getAsset( item->getAssetUUID(), + LLWearableList::instance().getAsset(item->getAssetUUID(), item->getName(), item->getType(), LLWearableBridge::onRemoveFromAvatarArrived, @@ -5073,7 +4933,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) LLViewerInventoryItem *obj_item = obj_item_array.get(i); if (get_is_item_worn(obj_item->getUUID())) { - LLVOAvatar::detachAttachmentIntoInventory(obj_item->getLinkedUUID()); + LLVOAvatarSelf::detachAttachmentIntoInventory(obj_item->getLinkedUUID()); } } } @@ -5085,7 +4945,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) LLViewerInventoryItem *gest_item = gest_item_array.get(i); if (get_is_item_worn(gest_item->getUUID())) { - LLGestureMgr::instance().deactivateGesture( gest_item_array.get(i)->getLinkedUUID() ); + LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() ); gInventory.updateItem( gest_item ); gInventory.notifyObservers(); } @@ -5093,58 +4953,26 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) } } } - delete category_id; - category_id = NULL; } BOOL LLWearableBridge::renameItem(const std::string& new_name) { - if (get_is_item_worn(getItem())) + if (get_is_item_worn(mUUID)) { gAgentWearables.setWearableName( mUUID, new_name ); } return LLItemBridge::renameItem(new_name); } -BOOL LLWearableBridge::isItemRemovable() -{ - LLInventoryModel* model = mInventoryPanel->getModel(); - if (!model) - { - return FALSE; - } -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g - if ( (rlv_handler_t::isEnabled()) && (RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) && (!RlvFolderLocks::instance().canRemoveItem(mUUID)) ) - { - return FALSE; - } -// [/RLVa:KB] -// [RLVa:KB] - Checked: 2011-03-29 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g - if ( (rlv_handler_t::isEnabled()) && (RlvFolderLocks::instance().hasLockedFolder(RLV_LOCK_ANY)) && (!RlvFolderLocks::instance().canRemoveItem(mUUID)) ) - { - return FALSE; - } -// [/RLVa:KB] - const LLInventoryObject *obj = model->getItem(mUUID); - if (obj && obj->getIsLinkType()) - { - return TRUE; - } - // - //if(gAgentWearables.isWearingItem(mUUID)) return FALSE; - // - return LLInvFVBridge::isItemRemovable(); -} - std::string LLWearableBridge::getLabelSuffix() const { - if (get_is_item_worn(getItem())) + if (get_is_item_worn(mUUID)) { if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canRemove(getItem())) ) { - return LLItemBridge::getLabelSuffix() + " (locked)"; + return LLItemBridge::getLabelSuffix() + LLTrans::getString("locked"); } - return LLItemBridge::getLabelSuffix() + " (worn)"; + return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn"); } else { @@ -5158,74 +4986,36 @@ LLUIImagePtr LLWearableBridge::getIcon() const } // virtual -void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLWearableBridge::performAction(LLInventoryModel* model, std::string action) { - if ("wear" == action) + if (isAddAction(action)) { wearOnAvatar(); } + else if ("wear_add" == action) + { + wearAddOnAvatar(); + } else if ("edit" == action) { editOnAvatar(); return; } - else if ("take_off" == action) + else if (isRemoveAction(action)) { - if(gAgentWearables.isWearingItem(mUUID)) - { - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - LLWearableBridge::onRemoveFromAvatarArrived, - new OnRemoveStruct(item->getLinkedUUID())); - } - } + removeFromAvatar(); + return; } - else LLItemBridge::performAction(folder, model, action); + else LLItemBridge::performAction(model, action); } void LLWearableBridge::openItem() { - if( isItemInTrash() ) + LLViewerInventoryItem* item = getItem(); + + if (item) { - LLNotificationsUtil::add("CannotWearTrash"); - } - else if(isAgentInventory()) - { - if (gAgentWearables.isWearingItem(mUUID)) - { - performAction(NULL, NULL, "take_off"); - } - else - { - performAction(NULL, NULL, "wear"); - } - } - else - { - // must be in the inventory library. copy it to our inventory - // and put it on right away. - LLViewerInventoryItem* item = getItem(); - if(item && item->isComplete()) - { - LLPointer cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else if(item) - { - // *TODO: We should fetch the item details, and then do - // the operation above. - LLNotificationsUtil::add("CannotWearInfoNotComplete"); - } + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } } @@ -5250,6 +5040,10 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) can_open = (item->getType() != LLAssetType::AT_CLOTHING) && (item->getType() != LLAssetType::AT_BODYPART); } + if (isLinkedObjectMissing()) + { + can_open = FALSE; + } if (can_open) { addOpenRightClickMenuOption(items); @@ -5264,17 +5058,16 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) getClipboardEntries(true, items, disabled_items, flags); - items.push_back(std::string("Wearable Separator")); + items.push_back(std::string("Wearable And Object Separator")); - items.push_back(std::string("Wearable Wear")); items.push_back(std::string("Wearable Edit")); -// [RLVa:KB] - Checked: 2011-09-16 (RLVa-1.1.4a) | Added: RLVa-1.1.4a - if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.canRemove(item)) ) +/*// [RLVa:KB] - Checked: 2011-09-16 (RLVa-1.1.4a) | Added: RLVa-1.1.4a + if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canRemove(item)) ) { - disabled_items.push_back(std::string("Wearable Wear")); + disabled_items.push_back(std::string("Wearable And Object Wear")); disabled_items.push_back(std::string("Wearable Edit")); } -// [/RLVa:KB] +// [/RLVa:KB]*/ if ((flags & FIRST_SELECTED_ITEM) == 0) { @@ -5295,25 +5088,25 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { case LLAssetType::AT_CLOTHING: items.push_back(std::string("Take Off")); -// [RLVa:KB] - Checked: 2011-09-16 (RLVa-1.1.4a) | Added: RLVa-1.1.4a +/*// [RLVa:KB] - Checked: 2011-09-16 (RLVa-1.1.4a) | Added: RLVa-1.1.4a if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canRemove(item)) ) disabled_items.push_back(std::string("Take Off")); -// [/RLVa:KB] +// [/RLVa:KB]*/ // Fallthrough since clothing and bodypart share wear options case LLAssetType::AT_BODYPART: if (get_is_item_worn(item->getUUID())) { - disabled_items.push_back(std::string("Wearable And Wear")); + disabled_items.push_back(std::string("Wearable And Object Wear")); disabled_items.push_back(std::string("Wearable Add")); } else { - items.push_back(std::string("Wearable Wear")); + items.push_back(std::string("Wearable And Object Wear")); disabled_items.push_back(std::string("Take Off")); disabled_items.push_back(std::string("Wearable Edit")); } - /*if (LLWearableType::getAllowMultiwear(mWearableType)) + if (LLWearableType::getAllowMultiwear(mWearableType)) { items.push_back(std::string("Wearable Add")); if (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) @@ -5321,7 +5114,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) disabled_items.push_back(std::string("Wearable Add")); } } - break;*/ + break; default: break; } @@ -5339,9 +5132,9 @@ BOOL LLWearableBridge::canWearOnAvatar(void* user_data) if(!self->isAgentInventory()) { LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem(); - if(!item || !item->isComplete()) return FALSE; + if(!item || !item->isFinished()) return FALSE; } - return (!get_is_item_worn(self->getItem())); + return (!get_is_item_worn(self->mUUID)); } // Called from menus @@ -5366,21 +5159,25 @@ void LLWearableBridge::wearOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - if(!isAgentInventory()) - { - LLPointer cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar(item); - } + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + } +} + +void LLWearableBridge::wearAddOnAvatar() +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + // Don't wear anything until initial wearables are loaded, can + // destroy clothing items. + if (!gAgentWearables.areWearablesLoaded()) + { + LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded"); + return; + } + + LLViewerInventoryItem* item = getItem(); + if(item) + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, false); } } @@ -5396,21 +5193,6 @@ void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userda { if(item->getAssetUUID() == wearable->getAssetID()) { - //RN: after discussing with Brashears, I disabled this code - //Metadata should reside in the item, not the asset - //And this code does not handle failed asset uploads properly - -// if(!wearable->isMatchedToInventoryItem(item)) -// { -// LLWearable* new_wearable = LLWearableList::instance().createWearableMatchedToInventoryItem( wearable, item ); -// -// // Now that we have an asset that matches the -// // item, update the item to point to the new -// // asset. -// item->setAssetUUID(new_wearable->getID()); -// item->updateAssetOnServer(); -// wearable = new_wearable; -// } gAgentWearables.setWearableItem(item, wearable); gInventory.notifyObservers(); //self->getFolderItem()->refreshFromRoot(); @@ -5424,13 +5206,40 @@ void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userda delete item_id; } +// static +// BAP remove the "add" code path once everything is fully COF-ified. +void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata ) +{ + LLUUID* item_id = (LLUUID*) userdata; + if(wearable) + { + LLViewerInventoryItem* item = NULL; + item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); + if(item) + { + if(item->getAssetUUID() == wearable->getAssetID()) + { + bool do_append = true; + gAgentWearables.setWearableItem(item, wearable, do_append); + gInventory.notifyObservers(); + //self->getFolderItem()->refreshFromRoot(); + } + else + { + llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl; + } + } + } + delete item_id; +} + // static BOOL LLWearableBridge::canEditOnAvatar(void* user_data) { LLWearableBridge* self = (LLWearableBridge*)user_data; if(!self) return FALSE; - return (get_is_item_worn(self->getItem())); + return (get_is_item_worn(self->mUUID)); } // static @@ -5445,19 +5254,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data) void LLWearableBridge::editOnAvatar() { - LLUUID linked_id = gInventory.getLinkedItemID(mUUID); - LLWearable* wearable = gAgentWearables.getWearableFromItemID(linked_id); - if( wearable ) - { - // Set the tab to the right wearable. - LLFloaterCustomize::setCurrentWearableType( wearable->getType() ); - - if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() ) - { - // Start Avatar Customization - gAgentCamera.changeCameraToCustomizeAvatar(); - } - } + LLAgentWearables::editWearable(mUUID); } // static @@ -5466,7 +5263,7 @@ BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data) LLWearableBridge* self = (LLWearableBridge*)user_data; if( self && (LLAssetType::AT_BODYPART != self->mAssetType) ) { - return get_is_item_worn(self->getItem()); + return get_is_item_worn( self->mUUID ); } return FALSE; } @@ -5476,16 +5273,17 @@ void LLWearableBridge::onRemoveFromAvatar(void* user_data) { LLWearableBridge* self = (LLWearableBridge*)user_data; if(!self) return; - if(get_is_item_worn(self->getItem())) + if(get_is_item_worn(self->mUUID)) { LLViewerInventoryItem* item = self->getItem(); if (item) { + LLUUID parent_id = item->getParentUUID(); LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - onRemoveFromAvatarArrived, - new OnRemoveStruct(LLUUID(self->mUUID))); + item->getName(), + item->getType(), + onRemoveFromAvatarArrived, + new OnRemoveStruct(LLUUID(self->mUUID))); } } } @@ -5503,16 +5301,67 @@ void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, LLWearableType::EType type = wearable->getType(); if( !(type==LLWearableType::WT_SHAPE || type==LLWearableType::WT_SKIN || type==LLWearableType::WT_HAIR || type==LLWearableType::WT_EYES) ) //&& - //!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) ) + //!((!gAgent.isTeen()) && ( type==LLWearableType::WT_UNDERPANTS || type==LLWearableType::WT_UNDERSHIRT )) ) { - gAgentWearables.removeWearable( type, false, 0 ); // TODO: MULTI-WEARABLE + bool do_remove_all = false; + U32 index = gAgentWearables.getWearableIndex(wearable); + gAgentWearables.removeWearable( type, do_remove_all, index ); } } } + + // Find and remove this item from the COF. + LLAppearanceMgr::instance().removeCOFItemLinks(item_id,false); + gInventory.notifyObservers(); + delete on_remove_struct; } +// static +void LLWearableBridge::removeAllClothesFromAvatar() +{ + // Fetch worn clothes (i.e. the ones in COF). + LLInventoryModel::item_array_t clothing_items; + LLInventoryModel::cat_array_t dummy; + LLIsType is_clothing(LLAssetType::AT_CLOTHING); + gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), + dummy, + clothing_items, + LLInventoryModel::EXCLUDE_TRASH, + is_clothing, + false); + + // Take them off by removing from COF. + for (LLInventoryModel::item_array_t::const_iterator it = clothing_items.begin(); it != clothing_items.end(); ++it) + { + LLAppearanceMgr::instance().removeItemFromAvatar((*it)->getUUID()); + } +} + +// static +void LLWearableBridge::removeItemFromAvatar(LLViewerInventoryItem *item) +{ + if (item) + { + LLWearableList::instance().getAsset(item->getAssetUUID(), + item->getName(), + item->getType(), + LLWearableBridge::onRemoveFromAvatarArrived, + new OnRemoveStruct(item->getUUID())); + } +} + +void LLWearableBridge::removeFromAvatar() +{ + if (get_is_item_worn(mUUID)) + { + LLViewerInventoryItem* item = getItem(); + removeItemFromAvatar(item); + } +} + + // +=================================================+ // | LLLinkItemBridge | // +=================================================+ @@ -5551,78 +5400,8 @@ void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Properties")); addDeleteContextMenuOptions(items, disabled_items); - } + } hide_context_entries(menu, items, disabled_items); - } - -// +=================================================+ -// | LLLinkBridge | -// +=================================================+ -// For broken folder links. -std::string LLLinkFolderBridge::sPrefix("Link: "); -LLUIImagePtr LLLinkFolderBridge::getIcon() const -{ - return LLUI::getUIImage("inv_link_folder.tga"); -} - -void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - // *TODO: Translate - lldebugs << "LLLink::buildContextMenu()" << llendl; - menuentry_vec_t items; - menuentry_vec_t disabled_items; - - if (isItemInTrash()) - { - addTrashContextMenuOptions(items, disabled_items); - } - else - { - items.push_back(std::string("Find Original")); - addDeleteContextMenuOptions(items, disabled_items); - } - hide_context_entries(menu, items, disabled_items); -} - -void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("goto" == action) - { - gotoItem(folder); - return; - } - LLItemBridge::performAction(folder,model,action); -} - -void LLLinkFolderBridge::gotoItem(LLFolderView *folder) -{ - const LLUUID &cat_uuid = getFolderID(); - if (!cat_uuid.isNull()) - { - if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid)) - { - if (LLInventoryModel* model = getInventoryModel()) - { - model->fetchDescendentsOf(cat_uuid); - } - base_folder->setOpen(TRUE); - folder->setSelectionFromRoot(base_folder,TRUE); - folder->scrollToShowSelection(); - } - } -} - -const LLUUID &LLLinkFolderBridge::getFolderID() const -{ - if (LLViewerInventoryItem *link_item = getItem()) - { - if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory()) - { - const LLUUID& cat_uuid = cat->getUUID(); - return cat_uuid; - } - } - return LLUUID::null; } // +=================================================+ @@ -5681,3 +5460,516 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } + +// +=================================================+ +// | LLLinkBridge | +// +=================================================+ +// For broken folder links. +std::string LLLinkFolderBridge::sPrefix("Link: "); +LLUIImagePtr LLLinkFolderBridge::getIcon() const +{ + LLFolderType::EType folder_type = LLFolderType::FT_NONE; + const LLInventoryObject *obj = getInventoryObject(); + if (obj) + { + LLViewerInventoryCategory* cat = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + cat = (LLViewerInventoryCategory*)model->getCategory(obj->getLinkedUUID()); + if (cat) + { + folder_type = cat->getPreferredType(); + } + } + } + return LLFolderBridge::getIcon(folder_type); +} + +void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + lldebugs << "LLLink::buildContextMenu()" << llendl; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if (isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Find Original")); + addDeleteContextMenuOptions(items, disabled_items); + } + hide_context_entries(menu, items, disabled_items); +} +void LLLinkFolderBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("goto" == action) + { + gotoItem(); + return; + } + LLItemBridge::performAction(model,action); +} +void LLLinkFolderBridge::gotoItem() +{ + const LLUUID &cat_uuid = getFolderID(); + if (!cat_uuid.isNull()) + { + if (LLFolderViewItem *base_folder = mRoot->getItemByID(cat_uuid)) + { + if (LLInventoryModel* model = getInventoryModel()) + { + model->fetchDescendentsOf(cat_uuid); + } + base_folder->setOpen(TRUE); + mRoot->setSelectionFromRoot(base_folder,TRUE); + mRoot->scrollToShowSelection(); + } + } +} +const LLUUID &LLLinkFolderBridge::getFolderID() const +{ + if (LLViewerInventoryItem *link_item = getItem()) + { + if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory()) + { + const LLUUID& cat_uuid = cat->getUUID(); + return cat_uuid; + } + } + return LLUUID::null; +} + +/******************************************************************************** + ** + ** BRIDGE ACTIONS + **/ + +// static +void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type, + const LLUUID& uuid, + LLInventoryModel* model) +{ + // Perform indirection in case of link. + const LLUUID& linked_uuid = gInventory.getLinkedItemID(uuid); + + LLInvFVBridgeAction* action = createAction(asset_type,linked_uuid,model); + if(action) + { + action->doIt(); + delete action; + } +} + +// static +void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model) +{ + llassert(model); + LLViewerInventoryItem* item = model->getItem(uuid); + llassert(item); + if (item) + { + LLAssetType::EType asset_type = item->getType(); + LLInvFVBridgeAction* action = createAction(asset_type,uuid,model); + if(action) + { + action->doIt(); + delete action; + } + } +} + +LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const +{ + if (mModel) + return (LLViewerInventoryItem*)mModel->getItem(mUUID); + return NULL; +} + +class LLTextureBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + open_texture(mUUID, std::string("Texture: ") + item->getName(), FALSE); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLTextureBridgeAction(){} +protected: + LLTextureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLSoundBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + if(!LLPreview::show(mUUID)) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewSoundRect"); + rect.translate(left - rect.mLeft, top - rect.mTop); + LLPreviewSound* preview = new LLPreviewSound("preview sound", + rect, + std::string("Sound: ") + item->getName(), + mUUID); + preview->setFocus(TRUE); + // Keep entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLSoundBridgeAction(){} +protected: + LLSoundBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLLandmarkBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // Opening (double-clicking) a landmark immediately teleports, + // but warns you the first time. + LLSD payload; + payload["asset_id"] = item->getAssetUUID(); + + payload["item_id"] = item->getUUID(); + + LLSD args; + args["LOCATION"] = item->getName(); + + LLNotificationsUtil::add("TeleportFromLandmark", args, payload); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLLandmarkBridgeAction(){} +protected: + LLLandmarkBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLCallingCardBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item && item->getCreatorUUID().notNull()) + { + bool online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); + LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLCallingCardBridgeAction(){} +protected: + LLCallingCardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + +}; + +class LLNotecardBridgeAction +: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + open_notecard(item, std::string("Notecard: ") + item->getName(), LLUUID::null, FALSE); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLNotecardBridgeAction(){} +protected: + LLNotecardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLGestureBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // See if we can bring an existing preview to the front + if(!LLPreview::show(mUUID)) + { + // TODO: save the rectangle + LLPreviewGesture* preview = LLPreviewGesture::show(std::string("Gesture: ") + item->getName(), mUUID, LLUUID::null); + preview->setFocus(TRUE); + + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLGestureBridgeAction(){} +protected: + LLGestureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLAnimationBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // See if we can bring an existing preview to the front + if(!LLPreview::show( mUUID )) + { + + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewAnimRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + LLPreviewAnim* preview = new LLPreviewAnim("preview anim", + rect, + std::string("Animation: ") + item->getName(), + mUUID, + LLPreviewAnim::NONE); + preview->setFocus(TRUE); + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLAnimationBridgeAction(){} +protected: + LLAnimationBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLObjectBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + /* + LLFloaterReg::showInstance("properties", mUUID); + */ + LLInvFVBridgeAction::doIt(); + } + virtual ~LLObjectBridgeAction(){} +protected: + LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLLSLTextBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { +// [RLVa:KB] - Checked: 2009-11-11 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a + if (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWSCRIPT)) + { + RlvNotifications::notifyBlockedViewScript(); + return; + } +// [/RLVa:KB] + LLViewerInventoryItem* item = getItem(); + if (item) + { + // See if we can bring an exiting preview to the front + if(!LLPreview::show(mUUID)) + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // There isn't one, so make a new preview + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewScriptRect"); + rect.translate(left - rect.mLeft, top - rect.mTop); + + LLPreviewLSL* preview = new LLPreviewLSL("preview lsl text", + rect, + std::string("Script: ") + item->getName(), + mUUID); + preview->setFocus(TRUE); + // keep onscreen + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLLSLTextBridgeAction(){} +protected: + LLLSLTextBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLWearableBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + wearOnAvatar(); + } + + virtual ~LLWearableBridgeAction(){} +protected: + LLWearableBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + BOOL isItemInTrash() const; + // return true if the item is in agent inventory. if false, it + // must be lost or in the inventory library. + BOOL isAgentInventory() const; + void wearOnAvatar(); +}; + +BOOL LLWearableBridgeAction::isItemInTrash() const +{ + if(!mModel) return FALSE; + const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH); + return mModel->isObjectDescendentOf(mUUID, trash_id); +} + +BOOL LLWearableBridgeAction::isAgentInventory() const +{ + if(!mModel) return FALSE; + if(gInventory.getRootFolderID() == mUUID) return TRUE; + return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); +} + +void LLWearableBridgeAction::wearOnAvatar() +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + LLViewerInventoryItem* item = getItem(); + if(item) + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + } +} + +LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type, + const LLUUID& uuid, + LLInventoryModel* model) +{ + LLInvFVBridgeAction* action = NULL; + switch(asset_type) + { + case LLAssetType::AT_TEXTURE: + action = new LLTextureBridgeAction(uuid,model); + break; + case LLAssetType::AT_SOUND: + action = new LLSoundBridgeAction(uuid,model); + break; + case LLAssetType::AT_LANDMARK: + action = new LLLandmarkBridgeAction(uuid,model); + break; + case LLAssetType::AT_CALLINGCARD: + action = new LLCallingCardBridgeAction(uuid,model); + break; + case LLAssetType::AT_OBJECT: + action = new LLObjectBridgeAction(uuid,model); + break; + case LLAssetType::AT_NOTECARD: + action = new LLNotecardBridgeAction(uuid,model); + break; + case LLAssetType::AT_ANIMATION: + action = new LLAnimationBridgeAction(uuid,model); + break; + case LLAssetType::AT_GESTURE: + action = new LLGestureBridgeAction(uuid,model); + break; + case LLAssetType::AT_LSL_TEXT: + action = new LLLSLTextBridgeAction(uuid,model); + break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + action = new LLWearableBridgeAction(uuid,model); + break; + default: + break; + } + return action; +} + +/************************************************************************/ +/* Recent Inventory Panel related classes */ +/************************************************************************/ +void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LLFolderBridge::buildContextMenu(menu, flags); + + menuentry_vec_t disabled_items, items = getMenuItems(); + + items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); + + hide_context_entries(menu, items, disabled_items); +} + +LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( + LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags /*= 0x00*/ ) const +{ + LLInvFVBridge* new_listener = NULL; + switch(asset_type) + { + case LLAssetType::AT_CATEGORY: + if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + { + // *TODO: Create a link folder handler instead if it is necessary + new_listener = LLInventoryFVBridgeBuilder::createBridge( + asset_type, + actual_asset_type, + inv_type, + inventory, + root, + uuid, + flags); + break; + } + new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); + break; + default: + new_listener = LLInventoryFVBridgeBuilder::createBridge( + asset_type, + actual_asset_type, + inv_type, + inventory, + root, + uuid, + flags); + } + return new_listener; + +} + + +// EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 381646b39..9d259e619 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -2,31 +2,25 @@ * @file llinventorybridge.h * @brief Implementation of the Inventory-Folder-View-Bridge classes. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,7 +32,6 @@ #include "llfoldervieweventlistener.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" -//#include "llinventoryview.h" #include "llviewercontrol.h" #include "llwearable.h" @@ -48,75 +41,9 @@ class LLMenuGL; class LLCallingCardObserver; class LLViewerJointAttachment; + typedef std::vector menuentry_vec_t; -struct LLAttachmentRezAction -{ - LLUUID mItemID; - S32 mAttachPt; -}; - -// [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i -// Moved from llinventorybridge.cpp because we need it in RlvForceWearLegacy -struct LLFoundData -{ - LLFoundData(const LLUUID& item_id, - const LLUUID& asset_id, - const std::string& name, - LLAssetType::EType asset_type) : - mItemID(item_id), - mAssetID(asset_id), - mName(name), - mAssetType(asset_type), - mWearable( NULL ) {} - - LLUUID mItemID; - LLUUID mAssetID; - std::string mName; - LLAssetType::EType mAssetType; - LLWearable* mWearable; -}; - -struct LLWearableHoldingPattern -{ - LLWearableHoldingPattern(BOOL fAddToOutfit) : mResolved(0), mAddToOutfit(fAddToOutfit) {} - ~LLWearableHoldingPattern() - { - for_each(mFoundList.begin(), mFoundList.end(), DeletePointer()); - mFoundList.clear(); - } - typedef std::list found_list_t; - found_list_t mFoundList; - S32 mResolved; - BOOL mAddToOutfit; -}; -// [/RLVa:KB] - -//helper functions -class LLShowProps -{ -public: - - static void showProperties(const LLUUID& uuid) - { - if(!LLFloaterProperties::show(uuid, LLUUID::null)) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PropertiesRect"); - rect.translate( left - rect.mLeft, top - rect.mTop ); - LLFloaterProperties* floater; - floater = new LLFloaterProperties("item properties", - rect, - "Inventory Item Properties", - uuid, - LLUUID::null); - // keep onscreen - gFloaterView->adjustToFitScreen(floater, FALSE); - } - } -}; - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInvFVBridge (& it's derived classes) // @@ -125,7 +52,7 @@ public: // // You'll want to call LLInvItemFVELister::createBridge() to actually create // an instance of this class. This helps encapsulate the -// funcationality a bit. (except for folders, you can create those +// functionality a bit. (except for folders, you can create those // manually...) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLInvFVBridge : public LLFolderViewEventListener @@ -137,6 +64,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00); virtual ~LLInvFVBridge() {} @@ -162,12 +90,13 @@ public: virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} + virtual void closeItem() {} virtual void previewItem() {openItem();} virtual void showProperties(); virtual BOOL isItemRenameable() const { return TRUE; } //virtual BOOL renameItem(const std::string& new_name) {} - virtual BOOL isItemRemovable(); - virtual BOOL isItemMovable(); + virtual BOOL isItemRemovable() const; + virtual BOOL isItemMovable() const; virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; //virtual BOOL removeItem() = 0; @@ -175,7 +104,7 @@ public: virtual void move(LLFolderViewEventListener* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const { return FALSE; } - virtual BOOL cutToClipboard() const { return FALSE; } + virtual void cutToClipboard(); virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} @@ -190,6 +119,9 @@ public: virtual LLInventoryType::EType getInventoryType() const { return mInvType; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + //-------------------------------------------------------------------- + // Convenience functions for adding various common menu options. + //-------------------------------------------------------------------- protected: virtual void addTrashContextMenuOptions(menuentry_vec_t &items, menuentry_vec_t &disabled_items); @@ -197,15 +129,14 @@ protected: menuentry_vec_t &disabled_items); virtual void addOpenRightClickMenuOption(menuentry_vec_t &items); protected: - LLInvFVBridge(LLInventoryPanel* inventory, /*LLFolderView* root,*/ const LLUUID& uuid); + LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); LLInventoryObject* getInventoryObject() const; LLInventoryModel* getInventoryModel() const; BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? - // return true if the item is in agent inventory. if false, it - // must be lost or in the inventory library. + BOOL isAgentInventory() const; // false if lost or in the inventory library BOOL isCOFFolder() const; // true if COF or descendent of virtual BOOL isItemPermissive() const; @@ -218,28 +149,48 @@ protected: const LLUUID& new_parent, BOOL restamp); void removeBatchNoCheck(LLDynamicArray& batch); - protected: - LLInventoryPanel* mInventoryPanel; - LLUUID mUUID; // item id + LLHandle mInventoryPanel; + LLFolderView* mRoot; + const LLUUID mUUID; // item id LLInventoryType::EType mInvType; BOOL mIsLink; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); }; class AIFilePicker; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInvFVBridgeBuilder +// +// This class intended to build Folder View Bridge via LLInvFVBridge::createBridge. +// It can be overridden with another way of creation necessary Inventory-Folder-View-Bridge. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryFVBridgeBuilder +{ +public: + virtual ~LLInventoryFVBridgeBuilder() {} + virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags = 0x00) const; +}; class LLItemBridge : public LLInvFVBridge { public: - LLItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) : - LLInvFVBridge(inventory, uuid) {} - - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + LLItemBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLInvFVBridge(inventory, root, uuid) {} + virtual void performAction(LLInventoryModel* model, std::string action); virtual void selectItem(); virtual void restoreItem(); virtual void restoreToWorld(); - virtual void gotoItem(LLFolderView *folder); + virtual void gotoItem(); virtual LLUIImagePtr getIcon() const; virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; @@ -261,26 +212,28 @@ public: LLViewerInventoryItem* getItem() const; protected: + BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; static void buildDisplayName(LLInventoryItem* item, std::string& name); + mutable std::string mDisplayName; }; - class LLFolderBridge : public LLInvFVBridge { public: LLFolderBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLInvFVBridge(inventory, /*root,*/ uuid), + LLInvFVBridge(inventory, root, uuid), mCallingCards(FALSE), mWearables(FALSE) {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); + virtual void closeItem(); virtual BOOL isItemRenameable() const; virtual void selectItem(); virtual void restoreItem(); @@ -293,6 +246,9 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); + BOOL removeSystemFolder(); + bool removeItemResponse(const LLSD& notification, const LLSD& response); + virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); @@ -301,23 +257,28 @@ public: EDragAndDropType cargo_type, void* cargo_data); - virtual BOOL isItemRemovable(); - virtual BOOL isItemMovable(); + virtual BOOL isItemRemovable() const; + virtual BOOL isItemMovable() const ; virtual BOOL isUpToDate() const; + virtual BOOL isItemCopyable() const; virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; - + virtual BOOL copyToClipboard() const; + static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); - static void createWearable(LLUUID parent_folder_id, LLWearableType::EType type); LLViewerInventoryCategory* getCategory() const; LLHandle getHandle() { mHandle.bind(this); return mHandle; } protected: - // menu callbacks + void buildContextMenuBaseOptions(U32 flags); + void buildContextMenuFolderOptions(U32 flags); + + //-------------------------------------------------------------------- + // Menu callbacks + //-------------------------------------------------------------------- static void pasteClipboard(void* user_data); static void createNewCategory(void* user_data); - static void createNewShirt(void* user_data); static void createNewPants(void* user_data); static void createNewShoes(void* user_data); @@ -337,46 +298,55 @@ protected: BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck); - void modifyOutfit(BOOL append, BOOL replace = FALSE); + void modifyOutfit(BOOL append); + void determineFolderType(); + menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items + + void dropToFavorites(LLInventoryItem* inv_item); + void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); + + //-------------------------------------------------------------------- + // Messy hacks for handling folder options + //-------------------------------------------------------------------- public: static LLHandle sSelf; static void staticFolderOptionsMenu(); - void folderOptionsMenu(); private: - BOOL mCallingCards; - BOOL mWearables; - LLHandle mMenu; + BOOL mCallingCards; + BOOL mWearables; menuentry_vec_t mItems; menuentry_vec_t mDisabledItems; LLRootHandle mHandle; }; - class LLTextureBridge : public LLItemBridge { public: virtual const std::string& getPrefix() { static std::string ret("Texture: ");return ret; } LLTextureBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type) : - LLItemBridge(inventory, /*root,*/ uuid) + LLItemBridge(inventory, root, uuid) { mInvType = type; } virtual LLUIImagePtr getIcon() const; virtual void openItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void performAction(LLInventoryModel* model, std::string action); + bool canSaveTexture(void); }; class LLSoundBridge : public LLItemBridge { public: LLSoundBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} virtual void openItem(); virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); @@ -390,10 +360,10 @@ public: static const std::string& prefix() { static std::string ret("Landmark: ");return ret; } virtual const std::string& getPrefix() { return prefix(); } LLLandmarkBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00); - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual LLUIImagePtr getIcon() const; virtual void openItem(); @@ -406,17 +376,15 @@ class LLCallingCardBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Calling Card: ");return ret; } LLCallingCardBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* folder, const LLUUID& uuid ); ~LLCallingCardBridge(); virtual std::string getLabelSuffix() const; //virtual const std::string& getDisplayName() const; virtual LLUIImagePtr getIcon() const; - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - //virtual void renameItem(const std::string& new_name); - //virtual BOOL removeItem(); virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data); @@ -431,9 +399,9 @@ class LLNotecardBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Notecard: ");return ret; } LLNotecardBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} virtual void openItem(); }; @@ -442,17 +410,18 @@ class LLGestureBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Gesture: ");return ret; } LLGestureBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} // Only suffix for gesture items, not task items, because only // gestures in your inventory can be active. virtual LLFontGL::StyleFlags getLabelStyle() const; virtual std::string getLabelSuffix() const; - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual BOOL removeItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + static void playGesture(const LLUUID& item_id); }; @@ -461,10 +430,10 @@ class LLAnimationBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Animation: ");return ret; } LLAnimationBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + LLItemBridge(inventory, root, uuid) {} + virtual void performAction(LLInventoryModel* model, std::string action); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void openItem(); }; @@ -475,20 +444,19 @@ class LLObjectBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Object: ");return ret; } LLObjectBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type, U32 flags); virtual LLUIImagePtr getIcon() const; - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual std::string getLabelSuffix() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL isItemRemovable(); virtual BOOL renameItem(const std::string& new_name); LLInventoryObject* getObject() const; protected: - static LLUUID sContextMenuItemID; // Only valid while the context menu is open. + static LLUUID sContextMenuItemID; // Only valid while the context menu is open. U32 mAttachPt; BOOL mIsMultiObject; }; @@ -498,28 +466,26 @@ class LLLSLTextBridge : public LLItemBridge public: virtual const std::string& getPrefix() { static std::string ret("Script: ");return ret; } LLLSLTextBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid ) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} virtual void openItem(); }; - class LLWearableBridge : public LLItemBridge { public: LLWearableBridge(LLInventoryPanel* inventory, - /*LLFolderView* root,*/ + LLFolderView* root, const LLUUID& uuid, LLAssetType::EType asset_type, LLInventoryType::EType inv_type, LLWearableType::EType wearable_type); virtual LLUIImagePtr getIcon() const; - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual std::string getLabelSuffix() const; - virtual BOOL isItemRemovable(); virtual BOOL renameItem(const std::string& new_name); virtual LLWearableType::EType getWearableType() const { return mWearableType; } @@ -528,13 +494,19 @@ public: static void onWearOnAvatarArrived( LLWearable* wearable, void* userdata ); void wearOnAvatar(); + static void onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata ); + void wearAddOnAvatar(); + static BOOL canEditOnAvatar( void* userdata ); // Access to editOnAvatar() from menu static void onEditOnAvatar( void* userdata ); void editOnAvatar(); static BOOL canRemoveFromAvatar( void* userdata ); static void onRemoveFromAvatar( void* userdata ); - static void onRemoveFromAvatarArrived( LLWearable* wearable, void* userdata ); + static void onRemoveFromAvatarArrived( LLWearable* wearable, void* userdata ); + static void removeItemFromAvatar(LLViewerInventoryItem *item); + static void removeAllClothesFromAvatar(); + void removeFromAvatar(); protected: LLAssetType::EType mAssetType; LLWearableType::EType mWearableType; @@ -544,9 +516,9 @@ class LLLinkItemBridge : public LLItemBridge { public: LLLinkItemBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} virtual const std::string& getPrefix() { return sPrefix; } virtual LLUIImagePtr getIcon() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); @@ -558,34 +530,103 @@ class LLLinkFolderBridge : public LLItemBridge { public: LLLinkFolderBridge(LLInventoryPanel* inventory, - //LLFolderView* root, + LLFolderView* root, const LLUUID& uuid) : - LLItemBridge(inventory, /*root,*/ uuid) {} + LLItemBridge(inventory, root, uuid) {} virtual const std::string& getPrefix() { return sPrefix; } virtual LLUIImagePtr getIcon() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); - virtual void gotoItem(LLFolderView *folder); + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void gotoItem(); protected: const LLUUID &getFolderID() const; static std::string sPrefix; }; + class LLMeshBridge : public LLItemBridge { - friend class LLInvFVBridge; + friend class LLInvFVBridge; public: - virtual LLUIImagePtr getIcon() const; - virtual void openItem(); - virtual void previewItem(); - virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual LLUIImagePtr getIcon() const; + virtual void openItem(); + virtual void previewItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: - LLMeshBridge(LLInventoryPanel* inventory, - const LLUUID& uuid) : - LLItemBridge(inventory, uuid) {} + LLMeshBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLItemBridge(inventory, root, uuid) {} }; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInvFVBridgeAction +// +// This is an implementation class to be able to +// perform action to view inventory items. +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInvFVBridgeAction +{ +public: + // This method is a convenience function which creates the correct + // type of bridge action based on some basic information. + static LLInvFVBridgeAction* createAction(LLAssetType::EType asset_type, + const LLUUID& uuid, + LLInventoryModel* model); + static void doAction(LLAssetType::EType asset_type, + const LLUUID& uuid, LLInventoryModel* model); + static void doAction(const LLUUID& uuid, LLInventoryModel* model); + + virtual void doIt() {}; + virtual ~LLInvFVBridgeAction() {} // need this because of warning on OSX +protected: + LLInvFVBridgeAction(const LLUUID& id, LLInventoryModel* model) : + mUUID(id), mModel(model) {} + LLViewerInventoryItem* getItem() const; +protected: + const LLUUID& mUUID; // item id + LLInventoryModel* mModel; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Recent Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Overridden version of the Inventory-Folder-View-Bridge for Folders +class LLRecentItemsFolderBridge : public LLFolderBridge +{ + friend class LLInvFVBridgeAction; +public: + // Creates context menu for Folders related to Recent Inventory Panel. + // Uses base logic and than removes from visible items "New..." menu items. + LLRecentItemsFolderBridge(LLInventoryType::EType type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLFolderBridge(inventory, root, uuid) + { + mInvType = type; + } + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); +}; + +// Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel +class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +{ +public: + // Overrides FolderBridge for Recent Inventory Panel. + // It use base functionality for bridges other than FolderBridge. + virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags = 0x00) const; +}; void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace = false); @@ -597,4 +638,13 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, BOOL drop, void (*callback)(S32, void*) = NULL, void* user_data = NULL); + +// Utility function to hide all entries except those in the list +// Can be called multiple times on the same menu (e.g. if multiple items +// are selected). If "append" is false, then only common enabled items +// are set as enabled. +void hide_context_entries(LLMenuGL& menu, + const menuentry_vec_t &entries_to_show, + const menuentry_vec_t &disabled_entries); + #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryclipboard.cpp b/indra/newview/llinventoryclipboard.cpp index 94ffcbd45..53da34f44 100644 --- a/indra/newview/llinventoryclipboard.cpp +++ b/indra/newview/llinventoryclipboard.cpp @@ -2,31 +2,25 @@ * @file llinventoryclipboard.cpp * @brief LLInventoryClipboard class implementation * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -47,6 +41,7 @@ LLInventoryClipboard LLInventoryClipboard::sInstance; ///---------------------------------------------------------------------------- LLInventoryClipboard::LLInventoryClipboard() +: mCutMode(false) { } @@ -77,6 +72,16 @@ void LLInventoryClipboard::store(const LLDynamicArray& inv_objects) } } +void LLInventoryClipboard::cut(const LLUUID& object) +{ + if(!mCutMode && !mObjects.empty()) + { + //looks like there are some stored items, reset clipboard state + reset(); + } + mCutMode = true; + add(object); +} void LLInventoryClipboard::retrieve(LLDynamicArray& inv_objects) const { inv_objects.reset(); @@ -90,6 +95,7 @@ void LLInventoryClipboard::retrieve(LLDynamicArray& inv_objects) const void LLInventoryClipboard::reset() { mObjects.reset(); + mCutMode = false; } // returns true if the clipboard has something pasteable in it. diff --git a/indra/newview/llinventoryclipboard.h b/indra/newview/llinventoryclipboard.h index 7a2cf15d6..b9f1451e5 100644 --- a/indra/newview/llinventoryclipboard.h +++ b/indra/newview/llinventoryclipboard.h @@ -2,31 +2,25 @@ * @file llinventoryclipboard.h * @brief LLInventoryClipboard class header file * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -60,6 +54,7 @@ public: // this method stores an array of objects void store(const LLDynamicArray& inventory_objects); + void cut(const LLUUID& object); // this method gets the objects in the clipboard by copying them // into the array provided. void retrieve(LLDynamicArray& inventory_objects) const; @@ -69,11 +64,13 @@ public: // returns true if the clipboard has something pasteable in it. BOOL hasContents() const; + bool isCutMode() const { return mCutMode; } protected: static LLInventoryClipboard sInstance; LLDynamicArray mObjects; + bool mCutMode; public: // please don't actually call these diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 3849286cd..a5bd75877 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -30,7 +30,7 @@ // viewer includes #include "llfoldervieweventlistener.h" -#include "llfolderview.h" +#include "llfolderviewitem.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" #include "llviewercontrol.h" @@ -44,12 +44,21 @@ #include "lltrans.h" LLInventoryFilter::FilterOps::FilterOps() : - mFilterTypes(0xffffffff), + mFilterObjectTypes(0xffffffffffffffffULL), + mFilterCategoryTypes(0xffffffffffffffffULL), + mFilterWearableTypes(0xffffffffffffffffULL), mMinDate(time_min()), mMaxDate(time_max()), mHoursAgo(0), mShowFolderState(SHOW_NON_EMPTY_FOLDERS), - mPermissions(PERM_NONE) {} + mPermissions(PERM_NONE), + mFilterTypes(FILTERTYPE_OBJECT), + mFilterWorn(false), + mFilterUUID(LLUUID::null), + mFilterLinks(FILTERLINK_INCLUDE_LINKS) +{ +} + ///---------------------------------------------------------------------------- /// Class LLInventoryFilter ///---------------------------------------------------------------------------- @@ -62,7 +71,6 @@ LLInventoryFilter::LLInventoryFilter(const std::string& name) mSubStringMatchOffset = 0; mFilterSubString.clear(); - mFilterWorn = false; mFilterGeneration = 0; mMustPassGeneration = S32_MAX; mMinRequiredGeneration = 0; @@ -82,36 +90,184 @@ LLInventoryFilter::~LLInventoryFilter() BOOL LLInventoryFilter::check(LLFolderViewItem* item) { - LLFolderViewEventListener* listener = item->getListener(); - const LLUUID& item_id = listener->getUUID(); - const LLInventoryObject *obj = gInventory.getObject(item_id); - if (isActive() && obj && obj->getIsLinkType()) + // If it's a folder and we're showing all folders, return TRUE automatically. + const BOOL is_folder = (dynamic_cast(item) != NULL); + if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { - // When filtering is active, omit links. - return FALSE; + return TRUE; } - time_t earliest; + const LLFolderViewEventListener* listener = item->getListener(); + const LLUUID item_id = listener ? listener->getUUID() : LLUUID::null; - earliest = time_corrected() - mFilterOps.mHoursAgo * 3600; - if (mFilterOps.mMinDate > time_min() && mFilterOps.mMinDate < earliest) - { - earliest = mFilterOps.mMinDate; - } - else if (!mFilterOps.mHoursAgo) - { - earliest = 0; - } mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; - BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE) - && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos) - && (mFilterWorn == false || gAgentWearables.isWearingItem(item_id) || - (gAgentAvatarp && gAgentAvatarp->isWearingAttachment(item_id))) - && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions) - && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate); + + const BOOL passed_filtertype = checkAgainstFilterType(item); + const BOOL passed_permissions = checkAgainstPermissions(item); + const BOOL passed_filterlink = checkAgainstFilterLinks(item); + const BOOL passed_wearable = !mFilterOps.mFilterWorn || (gAgentWearables.isWearingItem(item_id) || (gAgentAvatarp && gAgentAvatarp->isWearingAttachment(item_id))); + const BOOL passed = (passed_filtertype && + passed_permissions && + passed_filterlink && + passed_wearable && + (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + return passed; } +bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) +{ + // we're showing all folders, overriding filter + if (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS) + { + return true; + } + + const LLFolderViewEventListener* listener = folder->getListener(); + const LLUUID folder_id = listener->getUUID(); + + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) + { + // Can only filter categories for items in your inventory + // (e.g. versus in-world object contents). + const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (!cat) + return false; + LLFolderType::EType cat_type = cat->getPreferredType(); + if (cat_type != LLFolderType::FT_NONE && (1LL << cat_type & mFilterOps.mFilterCategoryTypes) == U64(0)) + return false; + } + + return true; +} + +BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const +{ + const LLFolderViewEventListener* listener = item->getListener(); + if (!listener) return FALSE; + + LLInventoryType::EType object_type = listener->getInventoryType(); + const LLUUID object_id = listener->getUUID(); + const LLInventoryObject *object = gInventory.getObject(object_id); + + const U32 filterTypes = mFilterOps.mFilterTypes; + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_OBJECT + // Pass if this item's type is of the correct filter type + if (filterTypes & FILTERTYPE_OBJECT) + { + // If it has no type, pass it, unless it's a link. + if (object_type == LLInventoryType::IT_NONE) + { + if (object && object->getIsLinkType()) + { + return FALSE; + } + } + else if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) + { + return FALSE; + } + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_UUID + // Pass if this item is the target UUID or if it links to the target UUID + if (filterTypes & FILTERTYPE_UUID) + { + if (!object) return FALSE; + + if (object->getLinkedUUID() != mFilterOps.mFilterUUID) + return FALSE; + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_DATE + // Pass if this item is within the date range. + if (filterTypes & FILTERTYPE_DATE) + { + const U16 HOURS_TO_SECONDS = 3600; + time_t earliest = time_corrected() - mFilterOps.mHoursAgo * HOURS_TO_SECONDS; + if (mFilterOps.mMinDate > time_min() && mFilterOps.mMinDate < earliest) + { + earliest = mFilterOps.mMinDate; + } + else if (!mFilterOps.mHoursAgo) + { + earliest = 0; + } + if (listener->getCreationDate() < earliest || + listener->getCreationDate() > mFilterOps.mMaxDate) + return FALSE; + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_WEARABLE + // Pass if this item is a wearable of the appropriate type + if (filterTypes & FILTERTYPE_WEARABLE) + { + LLWearableType::EType type = listener->getWearableType(); + if ((0x1LL << type & mFilterOps.mFilterWearableTypes) == 0) + { + return FALSE; + } + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_EMPTYFOLDERS + // Pass if this item is a folder and is not a system folder that should be hidden + if (filterTypes & FILTERTYPE_EMPTYFOLDERS) + { + if (object_type == LLInventoryType::IT_CATEGORY) + { + bool is_hidden_if_empty = LLViewerFolderType::lookupIsHiddenIfEmpty(listener->getPreferredType()); + if (is_hidden_if_empty) + { + // Force the fetching of those folders so they are hidden iff they really are empty... + gInventory.fetchDescendentsOf(object_id); + return FALSE; + } + } + } + + return TRUE; +} + +BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const +{ + const LLFolderViewEventListener* listener = item->getListener(); + if (!listener) return FALSE; + + PermissionMask perm = listener->getPermissionMask(); + const LLInvFVBridge *bridge = dynamic_cast(item->getListener()); + if (bridge && bridge->isLink()) + { + const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); + const LLViewerInventoryItem *linked_item = gInventory.getItem(linked_uuid); + if (linked_item) + perm = linked_item->getPermissionMask(); + } + return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; +} + +BOOL LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewItem* item) const +{ + const LLFolderViewEventListener* listener = item->getListener(); + if (!listener) return TRUE; + + const LLUUID object_id = listener->getUUID(); + const LLInventoryObject *object = gInventory.getObject(object_id); + if (!object) return TRUE; + + const BOOL is_link = object->getIsLinkType(); + if (is_link && (mFilterOps.mFilterLinks == FILTERLINK_EXCLUDE_LINKS)) + return FALSE; + if (!is_link && (mFilterOps.mFilterLinks == FILTERLINK_ONLY_LINKS)) + return FALSE; + return TRUE; +} + const std::string& LLInventoryFilter::getFilterSubString(BOOL trim) const { return mFilterSubString; @@ -125,20 +281,32 @@ std::string::size_type LLInventoryFilter::getStringMatchOffset() const // has user modified default filter params? BOOL LLInventoryFilter::isNotDefault() const { - return mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes - || mFilterSubString.size() - || mFilterWorn - || mFilterOps.mPermissions != mDefaultFilterOps.mPermissions - || mFilterOps.mMinDate != mDefaultFilterOps.mMinDate - || mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate - || mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo; + BOOL not_default = FALSE; + + not_default |= (mFilterOps.mFilterObjectTypes != mDefaultFilterOps.mFilterObjectTypes); + not_default |= (mFilterOps.mFilterCategoryTypes != mDefaultFilterOps.mFilterCategoryTypes); + not_default |= (mFilterOps.mFilterWearableTypes != mDefaultFilterOps.mFilterWearableTypes); + not_default |= (mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes); + not_default |= (mFilterOps.mFilterLinks != mDefaultFilterOps.mFilterLinks); + not_default |= (mFilterSubString.size()); + not_default |= (mFilterOps.mFilterWorn != mDefaultFilterOps.mFilterWorn); + not_default |= (mFilterOps.mPermissions != mDefaultFilterOps.mPermissions); + not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); + not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); + not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); + + return not_default; } BOOL LLInventoryFilter::isActive() const { - return mFilterOps.mFilterTypes != 0xffffffff + return mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL + || mFilterOps.mFilterCategoryTypes != 0xffffffffffffffffULL + || mFilterOps.mFilterWearableTypes != 0xffffffffffffffffULL + || mFilterOps.mFilterTypes != FILTERTYPE_OBJECT + || mFilterOps.mFilterLinks != FILTERLINK_INCLUDE_LINKS || mFilterSubString.size() - || mFilterWorn + || mFilterOps.mFilterWorn != false || mFilterOps.mPermissions != PERM_NONE || mFilterOps.mMinDate != time_min() || mFilterOps.mMaxDate != time_max() @@ -157,15 +325,15 @@ BOOL LLInventoryFilter::isModifiedAndClear() return ret; } -void LLInventoryFilter::setFilterTypes(U32 types) +void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) { - if (mFilterOps.mFilterTypes != types) + if (current_types != types) { // keep current items only if no type bits getting turned off - BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types); - BOOL more_bits_set = (~mFilterOps.mFilterTypes & types); - - mFilterOps.mFilterTypes = types; + bool fewer_bits_set = (current_types & ~types) != 0; + bool more_bits_set = (~current_types & types) != 0; + + current_types = types; if (more_bits_set && fewer_bits_set) { // neither less or more restrive, both simultaneously @@ -181,10 +349,46 @@ void LLInventoryFilter::setFilterTypes(U32 types) { setModified(FILTER_MORE_RESTRICTIVE); } - } } +void LLInventoryFilter::setFilterObjectTypes(U64 types) +{ + updateFilterTypes(types, mFilterOps.mFilterObjectTypes); + mFilterOps.mFilterTypes |= FILTERTYPE_OBJECT; +} + +void LLInventoryFilter::setFilterCategoryTypes(U64 types) +{ + updateFilterTypes(types, mFilterOps.mFilterCategoryTypes); + mFilterOps.mFilterTypes |= FILTERTYPE_CATEGORY; +} + +void LLInventoryFilter::setFilterWearableTypes(U64 types) +{ + updateFilterTypes(types, mFilterOps.mFilterWearableTypes); + mFilterOps.mFilterTypes |= FILTERTYPE_WEARABLE; +} + +void LLInventoryFilter::setFilterEmptySystemFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; +} + +void LLInventoryFilter::setFilterUUID(const LLUUID& object_id) +{ + if (mFilterOps.mFilterUUID == LLUUID::null) + { + setModified(FILTER_MORE_RESTRICTIVE); + } + else + { + setModified(FILTER_RESTART); + } + mFilterOps.mFilterUUID = object_id; + mFilterOps.mFilterTypes = FILTERTYPE_UUID; +} + void LLInventoryFilter::setFilterSubString(const std::string& string) { std::string filter_sub_string_new = string; @@ -215,6 +419,20 @@ void LLInventoryFilter::setFilterSubString(const std::string& string) { setModified(FILTER_RESTART); } + + // Cancel out UUID once the search string is modified + if (mFilterOps.mFilterTypes == FILTERTYPE_UUID) + { + mFilterOps.mFilterTypes &= ~FILTERTYPE_UUID; + mFilterOps.mFilterUUID == LLUUID::null; + setModified(FILTER_RESTART); + } + + // Cancel out filter links once the search string is modified + // Singu Note: No, don't do this... + { + //mFilterOps.mFilterLinks = FILTERLINK_INCLUDE_LINKS; + } } } @@ -256,6 +474,7 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date); setModified(); } + mFilterOps.mFilterTypes |= FILTERTYPE_DATE; } void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) @@ -270,12 +489,18 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) setDateRange(0, time_max()); setModified(); } + mFilterOps.mFilterTypes |= FILTERTYPE_DATE; } BOOL LLInventoryFilter::isSinceLogoff() const { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && - (mFilterOps.mMaxDate == time_max()); + bool min_date = (mFilterOps.mMinDate == (time_t)mLastLogoff); + bool max_date = (mFilterOps.mMaxDate == time_max()); + bool is_filter = (mFilterOps.mFilterTypes & FILTERTYPE_DATE); + return min_date && max_date && is_filter; + //return (mFilterOps.mMinDate == (time_t)mLastLogoff) && + // (mFilterOps.mMaxDate == time_max()) && + // (mFilterOps.mFilterTypes & FILTERTYPE_DATE); } void LLInventoryFilter::clearModified() @@ -313,7 +538,22 @@ void LLInventoryFilter::setHoursAgo(U32 hours) setModified(FILTER_RESTART); } } + mFilterOps.mFilterTypes |= FILTERTYPE_DATE; } + +void LLInventoryFilter::setFilterLinks(U64 filter_links) +{ + if (mFilterOps.mFilterLinks != filter_links) + { + if (mFilterOps.mFilterLinks == FILTERLINK_EXCLUDE_LINKS || + mFilterOps.mFilterLinks == FILTERLINK_ONLY_LINKS) + setModified(FILTER_MORE_RESTRICTIVE); + else + setModified(FILTER_LESS_RESTRICTIVE); + } + mFilterOps.mFilterLinks = filter_links; +} + void LLInventoryFilter::setShowFolderState(EFolderShow state) { if (mFilterOps.mShowFolderState != state) @@ -403,9 +643,9 @@ void LLInventoryFilter::setModified(EFilterBehavior behavior) } } -BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t) const +BOOL LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const { - return mFilterOps.mFilterTypes & (0x01 << t); + return mFilterOps.mFilterObjectTypes & (1LL << t); } const std::string& LLInventoryFilter::getFilterText() @@ -423,7 +663,7 @@ const std::string& LLInventoryFilter::getFilterText() S32 num_filter_types = 0; mFilterText.clear(); - if (isFilterWith(LLInventoryType::IT_ANIMATION)) + if (isFilterObjectTypesWith(LLInventoryType::IT_ANIMATION)) { filtered_types += " Animations,"; filtered_by_type = TRUE; @@ -435,7 +675,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_CALLINGCARD)) + if (isFilterObjectTypesWith(LLInventoryType::IT_CALLINGCARD)) { filtered_types += " Calling Cards,"; filtered_by_type = TRUE; @@ -447,7 +687,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_WEARABLE)) + if (isFilterObjectTypesWith(LLInventoryType::IT_WEARABLE)) { filtered_types += " Clothing,"; filtered_by_type = TRUE; @@ -459,7 +699,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_GESTURE)) + if (isFilterObjectTypesWith(LLInventoryType::IT_GESTURE)) { filtered_types += " Gestures,"; filtered_by_type = TRUE; @@ -471,7 +711,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_LANDMARK)) + if (isFilterObjectTypesWith(LLInventoryType::IT_LANDMARK)) { filtered_types += " Landmarks,"; filtered_by_type = TRUE; @@ -483,7 +723,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_NOTECARD)) + if (isFilterObjectTypesWith(LLInventoryType::IT_NOTECARD)) { filtered_types += " Notecards,"; filtered_by_type = TRUE; @@ -495,7 +735,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT)) + if (isFilterObjectTypesWith(LLInventoryType::IT_OBJECT) && isFilterObjectTypesWith(LLInventoryType::IT_ATTACHMENT)) { filtered_types += " Objects,"; filtered_by_type = TRUE; @@ -507,7 +747,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_LSL)) + if (isFilterObjectTypesWith(LLInventoryType::IT_LSL)) { filtered_types += " Scripts,"; filtered_by_type = TRUE; @@ -519,7 +759,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_SOUND)) + if (isFilterObjectTypesWith(LLInventoryType::IT_SOUND)) { filtered_types += " Sounds,"; filtered_by_type = TRUE; @@ -531,7 +771,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_TEXTURE)) + if (isFilterObjectTypesWith(LLInventoryType::IT_TEXTURE)) { filtered_types += " Textures,"; filtered_by_type = TRUE; @@ -543,7 +783,7 @@ const std::string& LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (isFilterWith(LLInventoryType::IT_SNAPSHOT)) + if (isFilterObjectTypesWith(LLInventoryType::IT_SNAPSHOT)) { filtered_types += " Snapshots,"; filtered_by_type = TRUE; @@ -588,7 +828,7 @@ const std::string& LLInventoryFilter::getFilterText() void LLInventoryFilter::toLLSD(LLSD& data) const { - data["filter_types"] = (LLSD::Integer)getFilterTypes(); + data["filter_types"] = (LLSD::Integer)getFilterObjectTypes(); data["min_date"] = (LLSD::Integer)getMinDate(); data["max_date"] = (LLSD::Integer)getMaxDate(); data["hours_ago"] = (LLSD::Integer)getHoursAgo(); @@ -603,7 +843,7 @@ void LLInventoryFilter::fromLLSD(LLSD& data) { if(data.has("filter_types")) { - setFilterTypes((U32)data["filter_types"].asInteger()); + setFilterObjectTypes((U32)data["filter_types"].asInteger()); } if(data.has("min_date") && data.has("max_date")) @@ -642,6 +882,16 @@ void LLInventoryFilter::fromLLSD(LLSD& data) } } +U64 LLInventoryFilter::getFilterObjectTypes() const +{ + return mFilterOps.mFilterObjectTypes; +} + +U64 LLInventoryFilter::getFilterCategoryTypes() const +{ + return mFilterOps.mFilterCategoryTypes; +} + BOOL LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; @@ -665,6 +915,10 @@ U32 LLInventoryFilter::getHoursAgo() const { return mFilterOps.mHoursAgo; } +U64 LLInventoryFilter::getFilterLinks() const +{ + return mFilterOps.mFilterLinks; +} LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const { return mFilterOps.mShowFolderState; diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 2d6ef39e1..b999843b5 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -51,8 +51,26 @@ public: FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one }; + enum EFilterType { + FILTERTYPE_NONE = 0, + FILTERTYPE_OBJECT = 0x1 << 0, // normal default search-by-object-type + FILTERTYPE_CATEGORY = 0x1 << 1, // search by folder type + FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it + FILTERTYPE_DATE = 0x1 << 3, // search by date range + FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + }; + + enum EFilterLink + { + FILTERLINK_INCLUDE_LINKS, // show links too + FILTERLINK_EXCLUDE_LINKS, // don't show links + FILTERLINK_ONLY_LINKS // only show links + }; + enum ESortOrderType { + SO_NAME = 0, // Sort inventory by name SO_DATE = 0x1, // Sort inventory by date SO_FOLDERS_BY_NAME = 0x1 << 1, // Force folder sort by name SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top @@ -61,17 +79,26 @@ public: LLInventoryFilter(const std::string& name); virtual ~LLInventoryFilter(); - void setFilterTypes(U32 types); - BOOL isFilterWith(LLInventoryType::EType t) const; - U32 getFilterTypes() const { return mFilterOps.mFilterTypes; } - + // +-------------------------------------------------------------------+ + // + Parameters + // +-------------------------------------------------------------------+ + void setFilterObjectTypes(U64 types); + U64 getFilterObjectTypes() const; + U64 getFilterCategoryTypes() const; + BOOL isFilterObjectTypesWith(LLInventoryType::EType t) const; + void setFilterCategoryTypes(U64 types); + void setFilterUUID(const LLUUID &object_id); + void setFilterWearableTypes(U64 types); + void setFilterEmptySystemFolders(); + void updateFilterTypes(U64 types, U64& current_types); + void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } BOOL hasFilterString() const; - void setFilterWorn(bool worn) { mFilterWorn = worn; } - bool getFilterWorn() const { return mFilterWorn; } + void setFilterWorn(bool worn) { mFilterOps.mFilterWorn = worn; } + bool getFilterWorn() const { return mFilterOps.mFilterWorn; } void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -84,10 +111,18 @@ public: void setHoursAgo(U32 hours); U32 getHoursAgo() const; + void setFilterLinks(U64 filter_link); + U64 getFilterLinks() const; + // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ - BOOL check(LLFolderViewItem* item); + BOOL check(LLFolderViewItem* item); + bool checkFolder(const LLFolderViewFolder* folder); + BOOL checkAgainstFilterType(const LLFolderViewItem* item) const; + BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; + BOOL checkAgainstFilterLinks(const LLFolderViewItem* item) const; + std::string::size_type getStringMatchOffset() const; // +-------------------------------------------------------------------+ @@ -146,12 +181,20 @@ private: struct FilterOps { FilterOps(); - U32 mFilterTypes; + U32 mFilterTypes; + + U64 mFilterObjectTypes; // For _OBJECT + U64 mFilterWearableTypes; + U64 mFilterCategoryTypes; // For _CATEGORY + LLUUID mFilterUUID; // for UUID + time_t mMinDate; time_t mMaxDate; U32 mHoursAgo; EFolderShow mShowFolderState; PermissionMask mPermissions; + U64 mFilterLinks; + bool mFilterWorn; }; U32 mOrder; @@ -163,21 +206,19 @@ private: std::string::size_type mSubStringMatchOffset; std::string mFilterSubString; std::string mFilterSubStringOrig; - bool mFilterWorn; - - const std::string mName; - S32 mFilterGeneration; - S32 mMustPassGeneration; - S32 mMinRequiredGeneration; - S32 mNextFilterGeneration; + const std::string mName; - S32 mFilterCount; - EFilterBehavior mFilterBehavior; + S32 mFilterGeneration; + S32 mMustPassGeneration; + S32 mMinRequiredGeneration; + S32 mNextFilterGeneration; - BOOL mModified; - BOOL mNeedTextRebuild; - std::string mFilterText; + S32 mFilterCount; + EFilterBehavior mFilterBehavior; + + BOOL mModified; + BOOL mNeedTextRebuild; + std::string mFilterText; }; - #endif diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index d9c508e03..ddcc57776 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -41,6 +41,7 @@ #include "message.h" // newview includes +#include "llappearancemgr.h" #include "llappviewer.h" //#include "llfirstuse.h" #include "llfloaterinventory.h" @@ -52,9 +53,11 @@ #include "llinventorybridge.h" #include "llinventoryclipboard.h" #include "llinventorymodel.h" +#include "llinventorypanel.h" #include "lllineeditor.h" #include "llmenugl.h" #include "llnotificationsutil.h" +#include "llpanelmaininventory.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" @@ -75,13 +78,13 @@ #include "llvoavatarself.h" #include "llwearablelist.h" +#include + // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) #include "rlvhandler.h" #include "rlvlocks.h" // [/RLVa:KB] -#include "cofmgr.h" - BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; @@ -164,7 +167,7 @@ void change_category_parent(LLInventoryModel* model, model->notifyObservers(); } -/*void remove_category(LLInventoryModel* model, const LLUUID& cat_id) +void remove_category(LLInventoryModel* model, const LLUUID& cat_id) { if (!model || !get_is_category_removable(model, cat_id)) { @@ -195,7 +198,7 @@ void change_category_parent(LLInventoryModel* model, const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); change_category_parent(model, cat, trash_id, TRUE); } -}*/ +} void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) { @@ -237,7 +240,7 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id) LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; LLInventoryCollectAllItems collect_all; - gInventory.collectDescendentsIf(LLCOFMgr::instance().getCOF(), cats, items, LLInventoryModel::EXCLUDE_TRASH, collect_all); + gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), cats, items, LLInventoryModel::EXCLUDE_TRASH, collect_all); for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) { @@ -313,13 +316,13 @@ BOOL get_can_item_be_worn(const LLUUID& id) if (!item) return FALSE; - if (LLCOFMgr::instance().isLinkInCOF(item->getLinkedUUID())) + if (LLAppearanceMgr::isLinkInCOF(item->getLinkedUUID())) { // an item having links in COF (i.e. a worn item) return FALSE; } - if (gInventory.isObjectDescendentOf(id, LLCOFMgr::instance().getCOF())) + if (gInventory.isObjectDescendentOf(id, LLAppearanceMgr::instance().getCOF())) { // a non-link object in COF (should not normally happen) return FALSE; @@ -363,12 +366,10 @@ BOOL get_can_item_be_worn(const LLUUID& id) // Not being worn yet. return TRUE; } - break; - - default: - break; + break; + default: + break; } - return FALSE; } @@ -379,6 +380,11 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) return FALSE; } + if(id == gInventory.getRootFolderID()) + { + return FALSE; + } + // Can't delete an item that's in the library. if(!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) { @@ -387,7 +393,7 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) // Disable delete from COF folder; have users explicitly choose "detach/take off", // unless the item is not worn but in the COF (i.e. is bugged). - if (LLCOFMgr::instance().getIsProtectedCOFItem(id)) + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(id)) { if (get_is_item_worn(id)) { @@ -415,7 +421,7 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) return TRUE; } -/*BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) { // NOTE: This function doesn't check the folder's children. // See LLFolderBridge::isItemRemovable for a function that does @@ -465,7 +471,7 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) } return TRUE; -}*/ +} BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) { @@ -492,6 +498,26 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) } +void show_item_profile(const LLUUID& item_uuid) +{ + LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); + if(!LLFloaterProperties::show(linked_uuid, LLUUID::null)) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PropertiesRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + LLFloaterProperties* floater; + floater = new LLFloaterProperties("item properties", + rect, + "Inventory Item Properties", + linked_uuid, + LLUUID::null); + // keep onscreen + gFloaterView->adjustToFitScreen(floater, FALSE); + } +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -641,6 +667,33 @@ bool LLNameCategoryCollector::operator()( return false; } +bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + // Valid COF items are: + // - links to wearables (body parts or clothing) + // - links to attachments + // - links to gestures + // - links to ensemble folders + LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); + if (linked_item) + { + LLAssetType::EType type = linked_item->getType(); + return (type == LLAssetType::AT_CLOTHING || + type == LLAssetType::AT_BODYPART || + type == LLAssetType::AT_GESTURE || + type == LLAssetType::AT_OBJECT); + } + else + { + LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); + // BAP remove AT_NONE support after ensembles are fully working? + return (linked_category && + ((linked_category->getPreferredType() == LLFolderType::FT_NONE) || + (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType())))); + } +} + bool LLFindWearables::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -686,6 +739,40 @@ bool LLFindWearablesEx::operator()(LLInventoryCategory* cat, LLInventoryItem* it return (bool) get_is_item_worn(item->getUUID()) == mIsWorn; } +bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (!item) return false; + if (item->getType() != LLAssetType::AT_CLOTHING && + item->getType() != LLAssetType::AT_BODYPART) + { + return false; + } + + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; +} + +void LLFindWearablesOfType::setType(LLWearableType::EType type) +{ + mWearableType = type; +} + +bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (item) + { + return !get_is_item_removable(&gInventory, item->getUUID()); + } + if (cat) + { + return !get_is_category_removable(&gInventory, cat->getUUID()); + } + + llwarns << "Not a category and not an item?" << llendl; + return false; +} ///---------------------------------------------------------------------------- /// LLAssetIDMatches diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index edae3872f..20bc90d0d 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -53,6 +53,8 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); +void show_item_profile(const LLUUID& item_uuid); + void change_item_parent(LLInventoryModel* model, LLViewerInventoryItem* item, const LLUUID& new_parent_id, @@ -262,6 +264,77 @@ protected: std::string mName; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindCOFValidItems +// +// Collects items that can be legitimately linked to in the COF. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindCOFValidItems : public LLInventoryCollectFunctor +{ +public: + LLFindCOFValidItems() {} + virtual ~LLFindCOFValidItems() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); + +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindByMask +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindByMask : public LLInventoryCollectFunctor +{ +public: + LLFindByMask(U64 mask) + : mFilterMask(mask) + {} + + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + //converting an inventory type to a bitmap filter mask + if(item && (mFilterMask & (1LL << item->getInventoryType())) ) + { + return true; + } + + return false; + } + +private: + U64 mFilterMask; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindNonLinksByMask +// +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindNonLinksByMask : public LLInventoryCollectFunctor +{ +public: + LLFindNonLinksByMask(U64 mask) + : mFilterMask(mask) + {} + + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if(item && !item->getIsLinkType() && (mFilterMask & (1LL << item->getInventoryType())) ) + { + return true; + } + + return false; + } + + void setFilterMask(U64 mask) + { + mFilterMask = mask; + } + +private: + U64 mFilterMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFindWearables // @@ -292,6 +365,52 @@ private: bool mIsWorn; }; +//Inventory collect functor collecting wearables of a specific wearable type +class LLFindWearablesOfType : public LLInventoryCollectFunctor +{ +public: + LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {} + virtual ~LLFindWearablesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + void setType(LLWearableType::EType type); + +private: + LLWearableType::EType mWearableType; +}; + +/** Filter out wearables-links */ +class LLFindActualWearablesOfType : public LLFindWearablesOfType +{ +public: + LLFindActualWearablesOfType(LLWearableType::EType type) : LLFindWearablesOfType(type) {} + virtual ~LLFindActualWearablesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (item && item->getIsLinkType()) return false; + return LLFindWearablesOfType::operator()(cat, item); + } +}; + +/* Filters out items of a particular asset type */ +class LLIsTypeActual : public LLIsType +{ +public: + LLIsTypeActual(LLAssetType::EType type) : LLIsType(type) {} + virtual ~LLIsTypeActual() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (item && item->getIsLinkType()) return false; + return LLIsType::operator()(cat, item); + } +}; + +// Collect non-removable folders and items. +class LLFindNonRemovableObjects : public LLInventoryCollectFunctor +{ +public: + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); +}; + /** Inventory Collector Functions ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c85b6fa75..62ab2d8e7 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2,73 +2,51 @@ * @file llinventorymodel.cpp * @brief Implementation of the inventory model used to track agent inventory. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - #include "llinventorymodel.h" -#include "llassetstorage.h" -#include "llcrc.h" -#include "lldir.h" -#include "llsys.h" -#include "llxfermanager.h" +#include "llagent.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llinventorypanel.h" +#include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" -#include "message.h" - -#include "llagent.h" -#include "llfloater.h" -#include "llfloaterinventory.h" -#include "llfocusmgr.h" #include "llinventorypanel.h" -#include "llviewerinventory.h" +#include "llnotificationsutil.h" +#include "llwindow.h" +#include "llviewercontrol.h" +#include "llpreview.h" #include "llviewermessage.h" -#include "llfoldertype.h" #include "llviewerfoldertype.h" #include "llviewerwindow.h" -#include "llviewerregion.h" #include "llappviewer.h" -#include "lldbstrings.h" -#include "llviewerstats.h" -#include "llmutelist.h" -#include "llnotificationsutil.h" +#include "llviewerregion.h" #include "llcallbacklist.h" -#include "llpreview.h" -#include "llviewercontrol.h" -#include "llvoavatar.h" -#include "llsdutil.h" +#include "llvoavatarself.h" #include "statemachine/aievent.h" -// -#include "llappviewer.h" // gLostItemsRoot -// -#include // [RLVa:KB] #include "rlvhandler.h" @@ -82,6 +60,7 @@ // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewers with link items support, former caches are incorrect. const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; +BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -262,6 +241,38 @@ const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(cons return NULL; } +// +// Search up the parent chain until we get to the specified parent, then return the first child category under it +// +const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const +{ + if (master_parent_id == obj_id) + { + return NULL; + } + + const LLViewerInventoryCategory* current_cat = getCategory(obj_id); + + if (current_cat == NULL) + { + current_cat = getCategory(getObject(obj_id)->getParentUUID()); + } + + while (current_cat != NULL) + { + const LLUUID& current_parent_id = current_cat->getParentUUID(); + + if (current_parent_id == master_parent_id) + { + return current_cat; + } + + current_cat = getCategory(current_parent_id); + } + + return NULL; +} + // Get the object by id. Returns NULL if not found. LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const { @@ -1109,6 +1120,7 @@ void LLInventoryModel::deleteObject(const LLUUID& id) } addChangedMask(LLInventoryObserver::REMOVE, id); obj = NULL; // delete obj + updateLinkedObjectsFromPurge(id); gInventory.notifyObservers(); } @@ -1125,6 +1137,25 @@ void LLInventoryModel::purgeObject(const LLUUID &id) } } +void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id) +{ + LLInventoryModel::item_array_t item_array = collectLinkedItems(baseobj_id); + + // REBUILD is expensive, so clear the current change list first else + // everything else on the changelist will also get rebuilt. + gInventory.notifyObservers(); + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + const LLViewerInventoryItem *linked_item = (*iter); + const LLUUID &item_id = linked_item->getUUID(); + if (item_id == baseobj_id) continue; + addChangedMask(LLInventoryObserver::REBUILD, item_id); + } + gInventory.notifyObservers(); +} + // This is a method which collects the descendents of the id // provided. If the category is not found, no action is // taken. This method goes through the long winded process of @@ -2008,6 +2039,10 @@ void LLInventoryModel::buildParentChildMap() llwarns << "Found " << lost << " lost categories." << llendl; } + const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); + sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin(); + + // Now the items. We allocated in the last step, so now all we // have to do is iterate over the items and put them in the right // place. @@ -2614,10 +2649,10 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, gInventory.notifyObservers(); // *HACK: Do the 'show' logic for a new item in the inventory. - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if(view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); + active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); } } @@ -2888,7 +2923,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLViewerInventoryItem* wearable_item; wearable_item = gInventory.getItem(wearable_ids[i]); - wear_inventory_item_on_avatar(wearable_item); + LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); } } @@ -3112,6 +3147,139 @@ void LLInventoryModel::setLibraryOwnerID(const LLUUID& val) mLibraryOwnerID = val; } +// static +BOOL LLInventoryModel::getIsFirstTimeInViewer2() +{ + // Do not call this before parentchild map is built. + if (!gInventory.mIsAgentInvUsable) + { + llwarns << "Parent Child Map not yet built; guessing as first time in viewer2." << llendl; + return TRUE; + } + + return sFirstTimeInViewer2; +} + +LLInventoryModel::item_array_t::iterator LLInventoryModel::findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) +{ + LLInventoryModel::item_array_t::iterator curr_item = items.begin(); + + while (curr_item != items.end()) + { + if ((*curr_item)->getUUID() == id) + { + break; + } + ++curr_item; + } + + return curr_item; +} + +// static +// * @param[in, out] items - vector with items to be updated. It should be sorted in a right way +// * before calling this method. +// * @param src_item_id - LLUUID of inventory item to be moved in new position +// * @param dest_item_id - LLUUID of inventory item before (or after) which source item should +// * be placed. +// * @param insert_before - bool indicating if src_item_id should be placed before or after +// * dest_item_id. Default is true. +void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id, bool insert_before) +{ + LLInventoryModel::item_array_t::iterator it_src = findItemIterByUUID(items, src_item_id); + LLInventoryModel::item_array_t::iterator it_dest = findItemIterByUUID(items, dest_item_id); + + // If one of the passed UUID is not in the item list, bail out + if ((it_src == items.end()) || (it_dest == items.end())) + return; + + // Erase the source element from the list, keep a copy before erasing. + LLViewerInventoryItem* src_item = *it_src; + items.erase(it_src); + + // Note: Target iterator is not valid anymore because the container was changed, so update it. + it_dest = findItemIterByUUID(items, dest_item_id); + + // Go to the next element if one wishes to insert after the dest element + if (!insert_before) + { + ++it_dest; + } + + // Reinsert the source item in the right place + if (it_dest != items.end()) + { + items.insert(it_dest, src_item); + } + else + { + // Append to the list if it_dest reached the end + items.push_back(src_item); + } +} + +//* @param[in] items vector of items in order to be saved. +/*void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + item->setSortField(++sortField); + item->setComplete(TRUE); + item->updateServer(FALSE); + + updateItem(item); + + // Tell the parent folder to refresh its sort order. + addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + notifyObservers(); +}*/ + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +/*class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer& a, const LLPointer& b) + { + return a->getSortField() < b->getSortField(); + } +};*/ + +/** + * Sorts passed items by LLViewerInventoryItem sort field. + * + * @param[in, out] items - array of items, not sorted. + */ +/*static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) +{ + static LLViewerInventoryItemSort sort_functor; + std::sort(items.begin(), items.end(), sort_functor); +}*/ + +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +/*void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + // ensure items are sorted properly before changing order. EXT-3498 + rearrange_item_order_by_sort_field(items); + + // update order + updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +}*/ // *NOTE: DEBUG functionality void LLInventoryModel::dumpInventory() const { diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index ae4e9451c..90e841fef 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -2,31 +2,25 @@ * @file llinventorymodel.h * @brief LLInventoryModel class header file * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,6 +30,7 @@ #include "llassettype.h" #include "llfoldertype.h" #include "lldarray.h" +#include "llframetimer.h" #include "llhttpclient.h" #include "lluuid.h" #include "llpermissionsflags.h" @@ -72,7 +67,10 @@ class LLInventoryCollectFunctor; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLInventoryModel { + LOG_CLASS(LLInventoryModel); public: + friend class LLInventoryModelFetchDescendentsResponder; + enum EHasChildren { CHILDREN_NO, @@ -90,8 +88,6 @@ public: fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; void result(const LLSD& content); void error(U32 status, const std::string& reason); - public: - typedef std::vector folder_ref_t; protected: LLSD mRequestSD; }; @@ -168,6 +164,10 @@ private: //-------------------------------------------------------------------- // Login //-------------------------------------------------------------------- +public: + static BOOL getIsFirstTimeInViewer2(); +private: + static BOOL sFirstTimeInViewer2; const static S32 sCurrentInvCacheVersion; // expected inventory cache version /** Initialization/Setup @@ -247,6 +247,9 @@ public: // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; + + // Get first descendant of the child object under the specified parent + const LLViewerInventoryCategory *getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const; // Get the object by id. Returns NULL if not found. // NOTE: Use the pointer returned for read operations - do @@ -335,28 +338,33 @@ public: // cache accounting in a fairly efficient manner. This method does // not notify observers (though maybe it should...) void purgeDescendentsOf(const LLUUID& id); - - // This method optimally removes the referenced categories and - // items from the current agent's inventory in the database. It - // performs all of the during deletion. The local representation - // is not removed. - void deleteFromServer(LLDynamicArray& category_ids, - LLDynamicArray& item_ids); - - // - // Misc Methods - // - - - // This method to prepares a set of mock inventory which provides - // minimal functionality before the actual arrival of inventory. - //void mock(const LLUUID& root_id); - +protected: + void updateLinkedObjectsFromPurge(const LLUUID& baseobj_id); + //-------------------------------------------------------------------- + // Reorder + //-------------------------------------------------------------------- +public: + // Changes items order by insertion of the item identified by src_item_id + // before (or after) the item identified by dest_item_id. Both items must exist in items array. + // Sorting is stored after method is finished. Only src_item_id is moved before (or after) dest_item_id. + // The parameter "insert_before" controls on which side of dest_item_id src_item_id gets rensinserted. + static void updateItemsOrder(LLInventoryModel::item_array_t& items, + const LLUUID& src_item_id, + const LLUUID& dest_item_id, + bool insert_before = true); + // Gets an iterator on an item vector knowing only the item UUID. + // Returns end() of the vector if not found. + static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - // call this method to request the inventory. - //void requestFromServer(const LLUUID& agent_id); + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + //void saveItemsOrder(const LLInventoryModel::item_array_t& items); + // Rearranges Landmarks inside Favorites folder. + // Moves source landmark before target one. + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); //-------------------------------------------------------------------- // Creation diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 9be69dffb..3fc36a290 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -439,6 +439,7 @@ void LLInventoryModelFetchDescendentsResponder::result(const LLSD& content) { cat->setVersion(version); cat->setDescendentCount(descendents); + cat->determineFolderType(); } } @@ -532,7 +533,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch(std::string url) U32 folder_count=0; U32 max_batch_size=5; - U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; + U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1; uuid_vec_t recursive_cats; diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index bf45db43d..55b2b29dd 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -56,7 +56,9 @@ public: STRUCTURE = 16, // structural change, eg, item or folder moved CALLING_CARD = 32, // online, grant status, cancel, etc change GESTURE = 64, - ALL = 0xffffffff + REBUILD = 128, // Item UI changed (e.g. item type different) + SORT = 256, // Folder needs to be resorted. + ALL = 0xffffffff }; LLInventoryObserver(); virtual ~LLInventoryObserver(); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index bc264cc1e..d957b9f94 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1,32 +1,26 @@ -/** - * @file llinventoryview.cpp - * @brief Implementation of the inventory view and associated stuff. +/* + * @file llinventorypanel.cpp + * @brief Implementation of the inventory panel and associated stuff. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,6 +31,7 @@ #include "llagent.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "lluictrlfactory.h" #include "llfloatercustomize.h" #include "llfolderview.h" @@ -48,6 +43,7 @@ #include "llvoavatarself.h" #include "llscrollcontainer.h" #include "llviewerassettype.h" +#include "llpanelmaininventory.h" #include "llsdserialize.h" @@ -57,12 +53,11 @@ static LLRegisterWidget r("inventory_panel"); - - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - +const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); +const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); +const std::string LLInventoryPanel::WORNITEMS_SORT_ORDER = std::string("WornItemsSortOrder"); +const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); +static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -88,10 +83,50 @@ protected: -const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); -const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); -const std::string LLInventoryPanel::WORNITEMS_SORT_ORDER = std::string("WornItemsSortOrder"); -const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInvPanelComplObserver +// +// Calls specified callback when all specified items become complete. +// +// Usage: +// observer = new LLInvPanelComplObserver(boost::bind(onComplete)); +// inventory->addObserver(observer); +// observer->reset(); // (optional) +// observer->watchItem(incomplete_item1_id); +// observer->watchItem(incomplete_item2_id); +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInvPanelComplObserver : public LLInventoryCompletionObserver +{ +public: + typedef boost::function callback_t; + + LLInvPanelComplObserver(callback_t cb) + : mCallback(cb) + { + } + + void reset(); + +private: + /*virtual*/ void done(); + + /// Called when all the items are complete. + callback_t mCallback; +}; + +void LLInvPanelComplObserver::reset() +{ + mIncomplete.clear(); + mComplete.clear(); +} + +void LLInvPanelComplObserver::done() +{ + mCallback(); +} LLInventoryPanel::LLInventoryPanel(const std::string& name, const std::string& sort_order_setting, @@ -100,48 +135,66 @@ LLInventoryPanel::LLInventoryPanel(const std::string& name, BOOL allow_multi_select, LLView *parent_view) : LLPanel(name, rect, TRUE), - mInventory(inventory), mInventoryObserver(NULL), + mCompletionObserver(NULL), mFolderRoot(NULL), mScroller(NULL), + mSortOrderSetting(sort_order_setting), + mInventory(inventory), mAllowMultiSelect(allow_multi_select), - mSortOrderSetting(sort_order_setting) + mViewsInitialized(false), + mInvFVBridgeBuilder(NULL) { + mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; + setBackgroundColor(gColors.getColor("InventoryBackgroundColor")); setBackgroundVisible(TRUE); setBackgroundOpaque(TRUE); } +void LLInventoryPanel::buildFolderView() +{ + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, + LLAssetType::AT_CATEGORY, + LLInventoryType::IT_CATEGORY, + this, + NULL, + LLUUID::null); + + mFolderRoot = createFolderView(new_listener, true/*params.use_label_suffix()*/); +} BOOL LLInventoryPanel::postBuild() { init_inventory_panel_actions(this); - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - mFolderRoot = new LLFolderView(getName(), NULL, folder_rect, LLUUID::null, this); - mFolderRoot->setAllowMultiSelect(mAllowMultiSelect); + buildFolderView(); + { + // scroller + LLRect scroller_view_rect = getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + mScroller = new LLScrollableContainerView(std::string("Inventory Scroller"), + scroller_view_rect, + mFolderRoot); + mScroller->setFollowsAll(); + mScroller->setReserveScrollCorner(TRUE); + addChild(mScroller); + mFolderRoot->setScrollContainer(mScroller); + } - // scroller - LLRect scroller_view_rect = getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - mScroller = new LLScrollableContainerView(std::string("Inventory Scroller"), - scroller_view_rect, - mFolderRoot); - mScroller->setFollowsAll(); - mScroller->setReserveScrollCorner(TRUE); - addChild(mScroller); - mFolderRoot->setScrollContainer(mScroller); - - // set up the callbacks from the inventory we're viewing, and then - // build everything. + // Set up the callbacks from the inventory we're viewing, and then build everything. mInventoryObserver = new LLInventoryPanelObserver(this); mInventory->addObserver(mInventoryObserver); - rebuildViewsFor(LLUUID::null, LLInventoryObserver::ADD); - // bit of a hack to make sure the inventory is open. - mFolderRoot->openFolder(std::string("My Inventory")); + mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); + mInventory->addObserver(mCompletionObserver); + + // Build view of inventory if we need default full hierarchy and inventory ready, + // otherwise wait for idle callback. + if (mInventory->isInventoryUsable() && !mViewsInitialized) + { + initializeViews(); + } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) { @@ -151,10 +204,8 @@ BOOL LLInventoryPanel::postBuild() { setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); } - mFolderRoot->setSortOrder(mFolderRoot->getFilter()->getSortOrder()); - - - return TRUE; + getFilter()->setFilterEmptySystemFolders(); + return LLPanel::postBuild(); } LLInventoryPanel::~LLInventoryPanel() @@ -168,9 +219,14 @@ LLInventoryPanel::~LLInventoryPanel() } } + gIdleCallbacks.deleteFunction(onIdle, this); + // LLView destructor will take care of the sub-views. mInventory->removeObserver(mInventoryObserver); + mInventory->removeObserver(mCompletionObserver); delete mInventoryObserver; + delete mCompletionObserver; + mScroller = NULL; } @@ -186,6 +242,18 @@ LLXMLNodePtr LLInventoryPanel::getXML(bool save_children) const return node; } +class LLInventoryRecentItemsPanel : public LLInventoryPanel +{ +public: + LLInventoryRecentItemsPanel(const std::string& name, + const std::string& sort_order_setting, + const LLRect& rect, + LLInventoryModel* inventory, + BOOL allow_multi_select, + LLView *parent_view); + +}; + LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { LLInventoryPanel* panel; @@ -202,7 +270,12 @@ LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac std::string sort_order(INHERIT_SORT_ORDER); node->getAttributeString("sort_order", sort_order); - panel = new LLInventoryPanel(name, sort_order, + if(name != "Recent Items") + panel = new LLInventoryPanel(name, sort_order, + rect, &gInventory, + allow_multi_select, parent); + else + panel = new LLInventoryRecentItemsPanel(name, sort_order, rect, &gInventory, allow_multi_select, parent); @@ -215,11 +288,8 @@ LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac void LLInventoryPanel::draw() { - // select the desired item (in case it wasn't loaded when the selection was requested) - if (mSelectThisID.notNull()) - { - setSelection(mSelectThisID, false); - } + // Select the desired item (in case it wasn't loaded when the selection was requested) + mFolderRoot->updateSelection(); LLPanel::draw(); } @@ -241,14 +311,17 @@ const LLInventoryFilter* LLInventoryPanel::getFilter() const return NULL; } -void LLInventoryPanel::setFilterTypes(U32 filter_types) +void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) { - getFilter()->setFilterTypes(filter_types); -} + if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT) + getFilter()->setFilterObjectTypes(types); + if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY) + getFilter()->setFilterCategoryTypes(types); +} -U32 LLInventoryPanel::getFilterTypes() const +U32 LLInventoryPanel::getFilterObjectTypes() const { - return mFolderRoot->getFilterTypes(); + return mFolderRoot->getFilterObjectTypes(); } U32 LLInventoryPanel::getFilterPermMask() const @@ -256,11 +329,17 @@ U32 LLInventoryPanel::getFilterPermMask() const return mFolderRoot->getFilterPermissions(); } + void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) { getFilter()->setFilterPermissions(filter_perm_mask); } +void LLInventoryPanel::setFilterWearableTypes(U64 types) +{ + getFilter()->setFilterWearableTypes(types); +} + void LLInventoryPanel::setFilterWorn(bool worn) { getFilter()->setFilterWorn(worn); @@ -276,6 +355,7 @@ const std::string LLInventoryPanel::getFilterSubString() return mFolderRoot->getFilterSubString(); } + void LLInventoryPanel::setSortOrder(U32 order) { getFilter()->setSortOrder(order); @@ -292,6 +372,11 @@ U32 LLInventoryPanel::getSortOrder() const return mFolderRoot->getSortOrder(); } +void LLInventoryPanel::requestSort() +{ + mFolderRoot->requestSort(); +} + void LLInventoryPanel::setSinceLogoff(BOOL sl) { getFilter()->setDateRangeLastLogoff(sl); @@ -302,6 +387,11 @@ void LLInventoryPanel::setHoursAgo(U32 hours) getFilter()->setHoursAgo(hours); } +void LLInventoryPanel::setFilterLinks(U64 filter_links) +{ + getFilter()->setFilterLinks(filter_links); +} + void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { getFilter()->setShowFolderState(show); @@ -319,8 +409,8 @@ void LLInventoryPanel::modelChanged(U32 mask) bool handled = false; - //if (!mViewsInitialized) return; - + if (!mViewsInitialized) return; + const LLInventoryModel* model = getModel(); if (!model) return; @@ -337,7 +427,7 @@ void LLInventoryPanel::modelChanged(U32 mask) // LLFolderViewFolder is derived from LLFolderViewItem so dynamic_cast from item // to folder is the fast way to get a folder without searching through folders tree. - //LLFolderViewFolder* view_folder = dynamic_cast(view_item); + LLFolderViewFolder* view_folder = dynamic_cast(view_item); ////////////////////////////// // LABEL Operation @@ -361,7 +451,7 @@ void LLInventoryPanel::modelChanged(U32 mask) ////////////////////////////// // REBUILD Operation // Destroy and regenerate the UI. - /*if (mask & LLInventoryObserver::REBUILD) + if (mask & LLInventoryObserver::REBUILD) { handled = true; if (model_item && view_item) @@ -370,7 +460,7 @@ void LLInventoryPanel::modelChanged(U32 mask) } view_item = buildNewViews(item_id); view_folder = dynamic_cast(view_item); - }*/ + } ////////////////////////////// // INTERNAL Operation @@ -386,13 +476,13 @@ void LLInventoryPanel::modelChanged(U32 mask) ////////////////////////////// // SORT Operation // Sort the folder. - /*if (mask & LLInventoryObserver::SORT) + if (mask & LLInventoryObserver::SORT) { if (view_folder) { view_folder->requestSort(); } - }*/ + } // We don't typically care which of these masks the item is actually flagged with, since the masks // may not be accurate (e.g. in the main inventory panel, I move an item from My Inventory into @@ -463,20 +553,86 @@ LLFolderView* LLInventoryPanel::getRootFolder() { return mFolderRoot; } + + +// static +void LLInventoryPanel::onIdle(void *userdata) +{ + if (!gInventory.isInventoryUsable()) + return; + + LLInventoryPanel *self = (LLInventoryPanel*)userdata; + // Inventory just initialized, do complete build + if (!self->mViewsInitialized) + { + self->initializeViews(); + } + if (self->mViewsInitialized) + { + gIdleCallbacks.deleteFunction(onIdle, (void*)self); + } +} + const LLUUID& LLInventoryPanel::getRootFolderID() const { return mFolderRoot->getListener()->getUUID(); } -void LLInventoryPanel::rebuildViewsFor(const LLUUID& id, U32 mask) + +void LLInventoryPanel::initializeViews() +{ + if (!gInventory.isInventoryUsable()) return; + + rebuildViewsFor(getRootFolderID()); + + mViewsInitialized = true; + + openStartFolderOrMyInventory(); + + // Special case for new user login + if (gAgent.isFirstLogin()) + { + // Auto open the user's library + LLFolderViewFolder* lib_folder = mFolderRoot->getFolderByID(gInventory.getLibraryRootFolderID()); + if (lib_folder) + { + lib_folder->setOpen(TRUE); + } + + // Auto close the user's my inventory folder + LLFolderViewFolder* my_inv_folder = mFolderRoot->getFolderByID(gInventory.getRootFolderID()); + if (my_inv_folder) + { + my_inv_folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); + } + } +} +LLFolderViewItem* LLInventoryPanel::rebuildViewsFor(const LLUUID& id) { // Destroy the old view for this ID so we can rebuild it. LLFolderViewItem* old_view = mFolderRoot->getItemByID(id); - if (old_view && id.notNull()) + if (old_view) { old_view->destroyView(); } - buildNewViews(id); + return buildNewViews(id); +} + +LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix) +{ + LLRect folder_rect(0, + 0, + getRect().getWidth(), + 0); + + LLFolderView* ret = new LLFolderView( + getName(), + folder_rect, + LLUUID::null, + this, + bridge); + ret->setAllowMultiSelect(mAllowMultiSelect); + return ret; } LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) @@ -484,6 +640,8 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br return new LLFolderViewFolder( bridge->getDisplayName(), bridge->getIcon(), + bridge->getOpenIcon(), + LLUI::getUIImage("inv_link_overlay.tga"), mFolderRoot, bridge); } @@ -493,76 +651,81 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge return new LLFolderViewItem( bridge->getDisplayName(), bridge->getIcon(), + bridge->getOpenIcon(), + LLUI::getUIImage("inv_link_overlay.tga"), bridge->getCreationDate(), mFolderRoot, bridge); } -void LLInventoryPanel::buildNewViews(const LLUUID& id) +LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { - LLInventoryObject* const objectp = gInventory.getObject(id); + LLInventoryObject const* objectp = gInventory.getObject(id); + LLUUID root_id = mFolderRoot->getListener()->getUUID(); + LLFolderViewFolder* parent_folder = NULL; LLFolderViewItem* itemp = NULL; - - if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)mFolderRoot->getItemByID(parent_id); - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { + if (id == root_id) + { + parent_folder = mFolderRoot; + } + else if (objectp) + { + const LLUUID &parent_id = objectp->getParentUUID(); + parent_folder = (LLFolderViewFolder*)mFolderRoot->getItemByID(parent_id); + + if (parent_folder) + { + if (objectp->getType() <= LLAssetType::AT_NONE || + objectp->getType() >= LLAssetType::AT_COUNT) + { llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << llendl; - } - else if ((objectp->getType() == LLAssetType::AT_CATEGORY) && - (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) // build new view for category - { - LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(objectp->getType(), + return NULL; + } + + if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), objectp->getType(), LLInventoryType::IT_CATEGORY, this, + mFolderRoot, objectp->getUUID()); - - if (new_listener) + if (new_listener) + { + LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); + if (folderp) + { + folderp->setItemSortOrder(mFolderRoot->getSortOrder()); + } + itemp = folderp; + } + } + else { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot->getSortOrder()); - } - itemp = folderp; - } - } - else // build new view for item - { - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = LLInvFVBridge::createBridge( - item->getType(), + // Build new view for item. + LLInventoryItem* item = (LLInventoryItem*)objectp; + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), item->getActualType(), item->getInventoryType(), - this, - item->getUUID(), - item->getFlags()); + this, + mFolderRoot, + item->getUUID(), + item->getFlags()); + if (new_listener) { itemp = createFolderViewItem(new_listener); } - } - - - - if (itemp) - { - if (parent_folder) - { - itemp->addToFolder(parent_folder, mFolderRoot); - } - else - { - llwarns << "Couldn't find parent folder for child " << itemp->getLabel() << llendl; - delete itemp; - } + } + + if (itemp) + { + itemp->addToFolder(parent_folder, mFolderRoot); + } } } @@ -587,7 +750,7 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) } } - if(items) + if(items && parent_folder) { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); @@ -599,6 +762,25 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) } mInventory->unlockDirectDescendentArrays(id); } + + return itemp; +} + +// bit of a hack to make sure the inventory is open. +void LLInventoryPanel::openStartFolderOrMyInventory() +{ + // Find My Inventory folder and open it up by name + for (LLView *child = mFolderRoot->getFirstChild(); child; child = mFolderRoot->findNextSibling(child)) + { + LLFolderViewFolder *fchild = dynamic_cast(child); + if (fchild + && fchild->getListener() + && fchild->getListener()->getUUID() == gInventory.getRootFolderID()) + { + fchild->setOpen(TRUE); + break; + } + } } struct LLConfirmPurgeData @@ -633,6 +815,10 @@ protected: const LLUUID& mID; }; +void LLInventoryPanel::onItemsCompletion() +{ + if (mFolderRoot) mFolderRoot->updateMenu(); +} void LLInventoryPanel::openSelected() { @@ -674,17 +860,47 @@ BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* accept, std::string& tooltip_msg) { + BOOL handled = FALSE; - BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - if (handled) + //if (mAcceptsDragAndDrop) { - mFolderRoot->setDragAndDropThisFrame(); + handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + + // If folder view is empty the (x, y) point won't be in its rect + // so the handler must be called explicitly. + // but only if was not handled before. See EXT-6746. + if (!handled && !mFolderRoot->hasVisibleChildren()) + { + handled = mFolderRoot->handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + + if (handled) + { + mFolderRoot->setDragAndDropThisFrame(); + } } return handled; } +void LLInventoryPanel::onFocusLost() +{ + // inventory no longer handles cut/copy/paste/delete + if (LLEditMenuHandler::gEditMenuHandler == mFolderRoot) + { + LLEditMenuHandler::gEditMenuHandler = NULL; + } + + LLPanel::onFocusLost(); +} + +void LLInventoryPanel::onFocusReceived() +{ + // inventory now handles cut/copy/paste/delete + LLEditMenuHandler::gEditMenuHandler = mFolderRoot; + + LLPanel::onFocusReceived(); +} void LLInventoryPanel::openAllFolders() { @@ -707,30 +923,52 @@ void LLInventoryPanel::openDefaultFolderForType(LLAssetType::EType type) void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus) { - LLFolderViewItem* itemp = mFolderRoot->getItemByID(obj_id); - if(itemp && itemp->getListener()) + // Don't select objects in COF (e.g. to prevent refocus when items are worn). + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (obj && obj->getParentUUID() == LLAppearanceMgr::instance().getCOF()) { - itemp->getListener()->arrangeAndSet(itemp, TRUE, take_keyboard_focus); - mSelectThisID.setNull(); return; } - else + mFolderRoot->setSelectionByID(obj_id, take_keyboard_focus); +} + +void LLInventoryPanel::setSelectCallback(const boost::function& items, BOOL user_action)>& cb) +{ + if (mFolderRoot) { - // save the desired item to be selected later (if/when ready) - mSelectThisID = obj_id; - } -} -void LLInventoryPanel::setSelectCallback(LLFolderView::SelectCallback callback, void* user_data) -{ - if (mFolderRoot) - { - mFolderRoot->setSelectCallback(callback, user_data); + mFolderRoot->setSelectCallback(cb); } } + void LLInventoryPanel::clearSelection() { mFolderRoot->clearSelection(); - mSelectThisID.setNull(); +} + +void LLInventoryPanel::onSelectionChange(const std::deque& items, BOOL user_action) +{ + // Schedule updating the folder view context menu when all selected items become complete (STORM-373). + mCompletionObserver->reset(); + for (std::deque::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLUUID id = (*it)->getListener()->getUUID(); + LLViewerInventoryItem* inv_item = mInventory->getItem(id); + + if (inv_item && !inv_item->isFinished()) + { + mCompletionObserver->watchItem(id); + } + } + + LLFolderView* fv = getRootFolder(); + if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename + { + fv->setNeedsAutoRename(FALSE); + if (items.size()) // new asset is visible and selected + { + fv->startRenamingSelectedItem(); + } + } } void LLInventoryPanel::createNewItem(const std::string& name, @@ -773,3 +1011,60 @@ void LLInventoryPanel::dumpSelectionInformation(void* user_data) LLInventoryPanel* iv = (LLInventoryPanel*)user_data; iv->mFolderRoot->dumpSelectionInformation(); } +// static +LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) +{ + LLInventoryPanel* res = NULL; + LLInventoryView* floater_inventory = LLInventoryView::getActiveInventory(); + if (!floater_inventory) + { + llwarns << "Could not find My Inventory floater" << llendl; + return FALSE; + } + res = floater_inventory ? floater_inventory->getActivePanel() : NULL; + if (res) + { + // Make sure the floater is not minimized (STORM-438). + if (floater_inventory && floater_inventory->isMinimized()) + { + floater_inventory->setMinimized(FALSE); + } + } + else if (auto_open) + { + floater_inventory->open(); + + res = floater_inventory->getActivePanel(); + } + return res; +} +void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) +{ + getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << folder_type)); +} + +BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) const +{ + return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); +} + + +/************************************************************************/ +/* Recent Inventory Panel related class */ +/************************************************************************/ +class LLInventoryRecentItemsPanel; +static const LLRecentInventoryBridgeBuilder RECENT_ITEMS_BUILDER; + + +LLInventoryRecentItemsPanel:: LLInventoryRecentItemsPanel(const std::string& name, + const std::string& sort_order_setting, + const LLRect& rect, + LLInventoryModel* inventory, + BOOL allow_multi_select, + LLView *parent_view) : + LLInventoryPanel(name, sort_order_setting,rect,inventory,allow_multi_select,parent_view) +{ + mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; +} + + diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 3a26926e4..4a2953f7f 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -47,16 +47,13 @@ #include "lluictrlfactory.h" #include -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryView -// -// This is the controller class specific for handling agent -// inventory. It deals with the buttons and views used to navigate as -// well as controls the behavior of the overall object. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +class LLFolderView; +class LLFolderViewFolder; +class LLFolderViewItem; +class LLInventoryFilter; class LLInventoryModel; class LLInvFVBridge; +class LLInventoryFVBridgeBuilder; class LLMenuBarGL; class LLCheckBoxCtrl; class LLSpinCtrl; @@ -65,19 +62,21 @@ class LLTextBox; class LLIconCtrl; class LLSaveFolderState; class LLSearchEditor; - +class LLInvPanelComplObserver; class LLInventoryPanel : public LLPanel { -public: +protected: LLInventoryPanel(const std::string& name, const std::string& sort_order_setting, const LLRect& rect, LLInventoryModel* inventory, BOOL allow_multi_select, LLView *parent_view = NULL); - ~LLInventoryPanel(); +public: + virtual ~LLInventoryPanel(); +public: LLInventoryModel* getModel() { return mInventory; } BOOL postBuild(); @@ -93,20 +92,24 @@ public: void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); + // LLUICtrl methods + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); // Call this method to set the selection. void openAllFolders(); void closeAllFolders(); void openDefaultFolderForType(LLAssetType::EType); void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); - void setSelectCallback(LLFolderView::SelectCallback callback, void* user_data); + void setSelectCallback(const boost::function& items, BOOL user_action)>& cb); void clearSelection(); LLInventoryFilter* getFilter(); const LLInventoryFilter* getFilter() const; - void setFilterTypes(U32 filter); - U32 getFilterTypes() const; + void setFilterTypes(U64 filter, LLInventoryFilter::EFilterType = LLInventoryFilter::FILTERTYPE_OBJECT); + U32 getFilterObjectTypes() const; void setFilterPermMask(PermissionMask filter_perm_mask); U32 getFilterPermMask() const; + void setFilterWearableTypes(U64 filter); void setFilterSubString(const std::string& string); const std::string getFilterSubString(); void setFilterWorn(bool worn); @@ -115,7 +118,8 @@ public: void setSinceLogoff(BOOL sl); void setHoursAgo(U32 hours); BOOL getSinceLogoff(); - + void setFilterLinks(U64 filter_links); + void setShowFolderState(LLInventoryFilter::EFolderShow show); LLInventoryFilter::EFolderShow getShowFolderState(); void setAllowMultiSelect(BOOL allow) { mFolderRoot->setAllowMultiSelect(allow); } @@ -123,17 +127,19 @@ public: void modelChanged(U32 mask); LLFolderView* getRootFolder(); LLScrollableContainerView* getScrollableContainer() { return mScroller; } - + + void onSelectionChange(const std::deque &items, BOOL user_action); // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); void openSelected(); void unSelectAll(); + + static void onIdle(void* user_data); -protected: - // Given the id and the parent, build all of the folder views. - void rebuildViewsFor(const LLUUID& id, U32 mask); - void buildNewViews(const LLUUID& id); + // Find whichever inventory panel is active / on top. + // "Auto_open" determines if we open an inventory panel if none are open. + static LLInventoryPanel *getActiveInventoryPanel(BOOL auto_open = TRUE); public: // TomY TODO: Move this elsewhere? @@ -147,13 +153,27 @@ public: U32 next_owner_perm = 0); protected: + void openStartFolderOrMyInventory(); // open the first level of inventory + void onItemsCompletion(); // called when selected items are complete + LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; - + LLInvPanelComplObserver* mCompletionObserver; BOOL mAllowMultiSelect; LLFolderView* mFolderRoot; LLScrollableContainerView* mScroller; + + /** + * Pointer to LLInventoryFVBridgeBuilder. + * + * It is set in LLInventoryPanel's constructor and can be overridden in derived classes with + * another implementation. + * Take into account it will not be deleted by LLInventoryPanel itself. + */ + const LLInventoryFVBridgeBuilder* mInvFVBridgeBuilder; + + //-------------------------------------------------------------------- // Sorting //-------------------------------------------------------------------- @@ -162,18 +182,36 @@ public: static const std::string RECENTITEMS_SORT_ORDER; static const std::string WORNITEMS_SORT_ORDER; static const std::string INHERIT_SORT_ORDER; - + void setSortOrder(U32 order); U32 getSortOrder() const; + void requestSort(); + private: const std::string mSortOrderSetting; - LLUUID mSelectThisID; // if non null, select this item - + + //-------------------------------------------------------------------- + // Hidden folders + //-------------------------------------------------------------------- public: + void addHideFolderType(LLFolderType::EType folder_type); + +public: + BOOL getIsViewsInitialized() const { return mViewsInitialized; } const LLUUID& getRootFolderID() const; protected: + // Builds the UI. Call this once the inventory is usable. + void initializeViews(); + LLFolderViewItem* rebuildViewsFor(const LLUUID& id); // Given the id and the parent, build all of the folder views. + + virtual void buildFolderView(); + LLFolderViewItem* buildNewViews(const LLUUID& id); + BOOL getIsHiddenFolderType(LLFolderType::EType folder_type) const; + + virtual LLFolderView* createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix); virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge); virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); + BOOL mViewsInitialized; // Views have been generated }; class LLInventoryView; @@ -193,11 +231,6 @@ void init_inventory_panel_actions(LLInventoryPanel *panel); class LLInventoryCategory; class LLInventoryItem; -//void wear_inventory_category_on_avatar(LLInventoryCategory* category); - -void wear_inventory_item_on_avatar(LLInventoryItem* item); -void wear_outfit_by_name(const std::string& name); -void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append); // These methods can open items without the inventory being visible void open_notecard(LLViewerInventoryItem* inv_item, const std::string& title, const LLUUID& object_id, BOOL show_keep_discard, const LLUUID& source_id = LLUUID::null, BOOL take_focus = TRUE); diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp index 4cfc2eaac..5e6f3e1b0 100644 --- a/indra/newview/lljoystickbutton.cpp +++ b/indra/newview/lljoystickbutton.cpp @@ -78,8 +78,7 @@ LLJoystick::LLJoystick( mHeldDown(FALSE), mHeldDownTimer() { - setHeldDownCallback(&LLJoystick::onHeldDown); - setCallbackUserData(this); + setHeldDownCallback(boost::bind(&LLJoystick::onHeldDownCallback,this)); } @@ -178,7 +177,7 @@ F32 LLJoystick::getElapsedHeldDownTime() } // static -void LLJoystick::onHeldDown(void *userdata) +void LLJoystick::onHeldDownCallback(void *userdata) { LLJoystick *self = (LLJoystick *)userdata; diff --git a/indra/newview/lljoystickbutton.h b/indra/newview/lljoystickbutton.h index 2d0492a11..74430f1c1 100644 --- a/indra/newview/lljoystickbutton.h +++ b/indra/newview/lljoystickbutton.h @@ -60,7 +60,7 @@ public: virtual void onHeldDown() = 0; F32 getElapsedHeldDownTime(); - static void onHeldDown(void *userdata); // called by llbutton callback handler + static void onHeldDownCallback(void *userdata); // called by llbutton callback handler void setInitialQuadrant(EJoystickQuadrant initial) { mInitialQuadrant = initial; }; virtual LLXMLNodePtr getXML(bool save_children = true) const; diff --git a/indra/newview/lllocalinventory.cpp b/indra/newview/lllocalinventory.cpp index 9b1494edf..c6578ed8c 100644 --- a/indra/newview/lllocalinventory.cpp +++ b/indra/newview/lllocalinventory.cpp @@ -105,7 +105,7 @@ void LLLocalInventory::open(LLUUID item_id) rect, "", item_id, - 0); + LLPreviewAnim::NONE); floaterp->setFocus(TRUE); gFloaterView->adjustToFitScreen(floaterp, FALSE); } @@ -333,8 +333,7 @@ void LLLocalInventory::loadInvCache(std::string filename) void LLLocalInventory::saveInvCache(std::string filename, LLFolderView* folder) { LLInventoryModel* model = &gInventory; - std::set selected_items; - folder->getSelectionList(selected_items); + std::set selected_items = folder->getSelectionList(); if(selected_items.size() < 1) { // No items selected? Wtfboom diff --git a/indra/newview/lllocaltextureobject.cpp b/indra/newview/lllocaltextureobject.cpp new file mode 100644 index 000000000..07ec0fab9 --- /dev/null +++ b/indra/newview/lllocaltextureobject.cpp @@ -0,0 +1,212 @@ +/** + * @file lllocaltextureobject.cpp + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lllocaltextureobject.h" + +#include "lltexlayer.h" +#include "llviewertexture.h" +#include "lltextureentry.h" +#include "lluuid.h" +#include "llwearable.h" + + +LLLocalTextureObject::LLLocalTextureObject() : + mIsBakedReady(FALSE), + mDiscard(MAX_DISCARD_LEVEL+1) +{ + mImage = NULL; +} + +LLLocalTextureObject::LLLocalTextureObject(LLViewerFetchedTexture* image, const LLUUID& id) : + mIsBakedReady(FALSE), + mDiscard(MAX_DISCARD_LEVEL+1) +{ + mImage = image; + gGL.getTexUnit(0)->bind(mImage); + mID = id; +} + +LLLocalTextureObject::LLLocalTextureObject(const LLLocalTextureObject& lto) : + mImage(lto.mImage), + mID(lto.mID), + mIsBakedReady(lto.mIsBakedReady), + mDiscard(lto.mDiscard) +{ + U32 num_layers = lto.getNumTexLayers(); + mTexLayers.reserve(num_layers); + for (U32 index = 0; index < num_layers; index++) + { + LLTexLayer* original_layer = lto.getTexLayer(index); + if (!original_layer) + { + llerrs << "could not clone Local Texture Object: unable to extract texlayer!" << llendl; + continue; + } + + LLTexLayer* new_layer = new LLTexLayer(*original_layer); + new_layer->setLTO(this); + mTexLayers.push_back(new_layer); + } +} + +LLLocalTextureObject::~LLLocalTextureObject() +{ +} + +LLViewerFetchedTexture* LLLocalTextureObject::getImage() const +{ + return mImage; +} + +LLTexLayer* LLLocalTextureObject::getTexLayer(U32 index) const +{ + if (index >= getNumTexLayers()) + { + return NULL; + } + + return mTexLayers[index]; +} + +LLTexLayer* LLLocalTextureObject::getTexLayer(const std::string &name) +{ + for( tex_layer_vec_t::iterator iter = mTexLayers.begin(); iter != mTexLayers.end(); iter++) + { + LLTexLayer *layer = *iter; + if (layer->getName().compare(name) == 0) + { + return layer; + } + } + + return NULL; +} + +U32 LLLocalTextureObject::getNumTexLayers() const +{ + return mTexLayers.size(); +} + +LLUUID LLLocalTextureObject::getID() const +{ + return mID; +} + +S32 LLLocalTextureObject::getDiscard() const +{ + return mDiscard; +} + +BOOL LLLocalTextureObject::getBakedReady() const +{ + return mIsBakedReady; +} + +void LLLocalTextureObject::setImage(LLViewerFetchedTexture* new_image) +{ + mImage = new_image; +} + +BOOL LLLocalTextureObject::setTexLayer(LLTexLayer *new_tex_layer, U32 index) +{ + if (index >= getNumTexLayers() ) + { + return FALSE; + } + + if (new_tex_layer == NULL) + { + return removeTexLayer(index); + } + + LLTexLayer *layer = new LLTexLayer(*new_tex_layer); + layer->setLTO(this); + + if (mTexLayers[index]) + { + delete mTexLayers[index]; + } + mTexLayers[index] = layer; + + return TRUE; +} + +BOOL LLLocalTextureObject::addTexLayer(LLTexLayer *new_tex_layer, LLWearable *wearable) +{ + if (new_tex_layer == NULL) + { + return FALSE; + } + + LLTexLayer *layer = new LLTexLayer(*new_tex_layer, wearable); + layer->setLTO(this); + mTexLayers.push_back(layer); + return TRUE; +} + +BOOL LLLocalTextureObject::addTexLayer(LLTexLayerTemplate *new_tex_layer, LLWearable *wearable) +{ + if (new_tex_layer == NULL) + { + return FALSE; + } + + LLTexLayer *layer = new LLTexLayer(*new_tex_layer, this, wearable); + layer->setLTO(this); + mTexLayers.push_back(layer); + return TRUE; +} + +BOOL LLLocalTextureObject::removeTexLayer(U32 index) +{ + if (index >= getNumTexLayers()) + { + return FALSE; + } + tex_layer_vec_t::iterator iter = mTexLayers.begin(); + iter += index; + + delete *iter; + mTexLayers.erase(iter); + return TRUE; +} + +void LLLocalTextureObject::setID(LLUUID new_id) +{ + mID = new_id; +} + +void LLLocalTextureObject::setDiscard(S32 new_discard) +{ + mDiscard = new_discard; +} + +void LLLocalTextureObject::setBakedReady(BOOL ready) +{ + mIsBakedReady = ready; +} + diff --git a/indra/newview/lllocaltextureobject.h b/indra/newview/lllocaltextureobject.h new file mode 100644 index 000000000..b9bfc5472 --- /dev/null +++ b/indra/newview/lllocaltextureobject.h @@ -0,0 +1,87 @@ +/** + * @file lllocaltextureobject.h + * @brief LLLocalTextureObject class header file + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LOCALTEXTUREOBJECT_H +#define LL_LOCALTEXTUREOBJECT_H + +#include + +#include "llviewertexture.h" + +class LLUUID; +class LLTexLayer; +class LLTextureEntry; +class LLTexLayerTemplate; +class LLWearable; + +// Stores all relevant information for a single texture +// assumed to have ownership of all objects referred to - +// will delete objects when being replaced or if object is destroyed. +class LLLocalTextureObject +{ +public: + LLLocalTextureObject(); + LLLocalTextureObject(LLViewerFetchedTexture* image, const LLUUID& id); + LLLocalTextureObject(const LLLocalTextureObject& lto); + ~LLLocalTextureObject(); + + LLViewerFetchedTexture* getImage() const; + LLTexLayer* getTexLayer(U32 index) const; + LLTexLayer* getTexLayer(const std::string &name); + U32 getNumTexLayers() const; + LLUUID getID() const; + S32 getDiscard() const; + BOOL getBakedReady() const; + + void setImage(LLViewerFetchedTexture* new_image); + BOOL setTexLayer(LLTexLayer *new_tex_layer, U32 index); + BOOL addTexLayer(LLTexLayer *new_tex_layer, LLWearable *wearable); + BOOL addTexLayer(LLTexLayerTemplate *new_tex_layer, LLWearable *wearable); + BOOL removeTexLayer(U32 index); + + void setID(LLUUID new_id); + void setDiscard(S32 new_discard); + void setBakedReady(BOOL ready); + +protected: + +private: + + LLPointer mImage; + // NOTE: LLLocalTextureObject should be the exclusive owner of mTexEntry and mTexLayer + // using shared pointers here only for smart assignment & cleanup + // do NOT create new shared pointers to these objects, or keep pointers to them around + typedef std::vector tex_layer_vec_t; + tex_layer_vec_t mTexLayers; + + LLUUID mID; + + BOOL mIsBakedReady; + S32 mDiscard; +}; + + #endif // LL_LOCALTEXTUREOBJECT_H + diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp index 822b18264..203f5ca0d 100644 --- a/indra/newview/llmorphview.cpp +++ b/indra/newview/llmorphview.cpp @@ -110,7 +110,7 @@ void LLMorphView::initialize() //----------------------------------------------------------------------------- void LLMorphView::shutdown() { - LLVOAvatar::onCustomizeEnd(); + LLVOAvatarSelf::onCustomizeEnd(); if (isAgentAvatarValid()) { diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 59996ca64..14b5fa13a 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -81,21 +81,21 @@ LLFloaterMove::LLFloaterMove(const LLSD& key) mTurnLeftButton = getChild("turn left btn"); mTurnLeftButton->setHeldDownDelay(MOVE_BUTTON_DELAY); - mTurnLeftButton->setHeldDownCallback( turnLeft ); + mTurnLeftButton->setHeldDownCallback( boost::bind(&LLFloaterMove::turnLeft, this) ); mTurnRightButton = getChild("turn right btn"); mTurnRightButton->setHeldDownDelay(MOVE_BUTTON_DELAY); - mTurnRightButton->setHeldDownCallback( turnRight ); + mTurnRightButton->setHeldDownCallback( boost::bind(&LLFloaterMove::turnRight, this) ); mMoveUpButton = getChild("move up btn"); childSetAction("move up btn",moveUp,NULL); mMoveUpButton->setHeldDownDelay(MOVE_BUTTON_DELAY); - mMoveUpButton->setHeldDownCallback( moveUp ); + mMoveUpButton->setHeldDownCallback( boost::bind(&LLFloaterMove::moveUp, this) ); mMoveDownButton = getChild("move down btn"); childSetAction("move down btn",moveDown,NULL); mMoveDownButton->setHeldDownDelay(MOVE_BUTTON_DELAY); - mMoveDownButton->setHeldDownCallback( moveDown ); + mMoveDownButton->setHeldDownCallback( boost::bind(&LLFloaterMove::moveDown, this) ); } // virtual diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 07adaa136..86294d46b 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -664,7 +664,7 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, std::string& msg, LLRect* sticky_rec LLVector3d mypos = gAgent.getPositionGlobal(); LLVector3d position = mClosestAgentPosition; - if ( LLFloaterAvatarList::getInstance() ) + if ( LLFloaterAvatarList::instanceExists() ) { LLAvatarListEntry *ent = LLFloaterAvatarList::getInstance()->getAvatarEntry(mClosestAgentToCursor); if ( NULL != ent ) diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp new file mode 100644 index 000000000..5bb69367a --- /dev/null +++ b/indra/newview/lloutfitobserver.cpp @@ -0,0 +1,153 @@ +/** + * @file lloutfitobserver.cpp + * @brief Outfit observer facade. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappearancemgr.h" +#include "lloutfitobserver.h" +#include "llinventorymodel.h" +#include "llviewerinventory.h" + +LLOutfitObserver::LLOutfitObserver() : + mCOFLastVersion(LLViewerInventoryCategory::VERSION_UNKNOWN) +{ + mItemNameHash.finalize(); + gInventory.addObserver(this); +} + +LLOutfitObserver::~LLOutfitObserver() +{ + if (gInventory.containsObserver(this)) + { + gInventory.removeObserver(this); + } +} + +void LLOutfitObserver::changed(U32 mask) +{ + if (!gInventory.isInventoryUsable()) + return; + + checkCOF(); + + checkBaseOutfit(); +} + +// static +S32 LLOutfitObserver::getCategoryVersion(const LLUUID& cat_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (!cat) + return LLViewerInventoryCategory::VERSION_UNKNOWN; + + return cat->getVersion(); +} + +// static +const std::string& LLOutfitObserver::getCategoryName(const LLUUID& cat_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (!cat) + return LLStringUtil::null; + + return cat->getName(); +} + +bool LLOutfitObserver::checkCOF() +{ + LLUUID cof = LLAppearanceMgr::getInstance()->getCOF(); + if (cof.isNull()) + return false; + + bool cof_changed = false; + LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cof); + if (item_name_hash != mItemNameHash) + { + cof_changed = true; + mItemNameHash = item_name_hash; + } + + S32 cof_version = getCategoryVersion(cof); + if (cof_version != mCOFLastVersion) + { + cof_changed = true; + mCOFLastVersion = cof_version; + } + + if (!cof_changed) + return false; + + // dirtiness state should be updated before sending signal + LLAppearanceMgr::getInstance()->updateIsDirty(); + mCOFChanged(); + + return true; +} + +void LLOutfitObserver::checkBaseOutfit() +{ + LLUUID baseoutfit_id = + LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); + + if (baseoutfit_id == mBaseOutfitId) + { + if (baseoutfit_id.isNull()) + return; + + const S32 baseoutfit_ver = getCategoryVersion(baseoutfit_id); + const std::string& baseoutfit_name = getCategoryName(baseoutfit_id); + + if (baseoutfit_ver == mBaseOutfitLastVersion + // renaming category doesn't change version, so it's need to check it + && baseoutfit_name == mLastBaseOutfitName) + return; + } + else + { + mBaseOutfitId = baseoutfit_id; + mBOFReplaced(); + + if (baseoutfit_id.isNull()) + return; + } + + mBaseOutfitLastVersion = getCategoryVersion(mBaseOutfitId); + mLastBaseOutfitName = getCategoryName(baseoutfit_id); + + LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance(); + // dirtiness state should be updated before sending signal + app_mgr.updateIsDirty(); + mBOFChanged(); + + if (mLastOutfitDirtiness != app_mgr.isOutfitDirty()) + { + if(!app_mgr.isOutfitDirty()) + { + mCOFSaved(); + } + mLastOutfitDirtiness = app_mgr.isOutfitDirty(); + } +} diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h new file mode 100644 index 000000000..87d4b0c99 --- /dev/null +++ b/indra/newview/lloutfitobserver.h @@ -0,0 +1,96 @@ +/** + * @file lloutfitobserver.h + * @brief Outfit observer facade. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_OUTFITOBSERVER_H +#define LL_OUTFITOBSERVER_H + +#include "llsingleton.h" +#include "llmd5.h" + +/** + * Outfit observer facade that provides simple possibility to subscribe on + * BOF(base outfit) replaced, BOF changed, COF(current outfit) changed events. + */ +class LLOutfitObserver: public LLInventoryObserver, public LLSingleton +{ +public: + virtual ~LLOutfitObserver(); + + friend class LLSingleton; + + virtual void changed(U32 mask); + + void notifyOutfitLockChanged() { mOutfitLockChanged(); } + + typedef boost::signals2::signal signal_t; + + void addBOFReplacedCallback(const signal_t::slot_type& cb) { mBOFReplaced.connect(cb); } + + void addBOFChangedCallback(const signal_t::slot_type& cb) { mBOFChanged.connect(cb); } + + void addCOFChangedCallback(const signal_t::slot_type& cb) { mCOFChanged.connect(cb); } + + void addCOFSavedCallback(const signal_t::slot_type& cb) { mCOFSaved.connect(cb); } + + void addOutfitLockChangedCallback(const signal_t::slot_type& cb) { mOutfitLockChanged.connect(cb); } + +protected: + LLOutfitObserver(); + + /** Get a version of an inventory category specified by its UUID */ + static S32 getCategoryVersion(const LLUUID& cat_id); + + static const std::string& getCategoryName(const LLUUID& cat_id); + + bool checkCOF(); + + void checkBaseOutfit(); + + //last version number of a COF category + S32 mCOFLastVersion; + + LLUUID mBaseOutfitId; + + S32 mBaseOutfitLastVersion; + std::string mLastBaseOutfitName; + + bool mLastOutfitDirtiness; + + LLMD5 mItemNameHash; + +private: + signal_t mBOFReplaced; + signal_t mBOFChanged; + signal_t mCOFChanged; + signal_t mCOFSaved; + + /** + * Signal for changing state of outfit lock. + */ + signal_t mOutfitLockChanged; +}; + +#endif /* LL_OUTFITOBSERVER_H */ diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index dd3e21c85..5dceed26c 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -587,7 +587,7 @@ void LLPanelAvatarSecondLife::onClickPartnerInfo(void *data) if (self->mPartnerID.notNull()) { LLFloaterAvatarInfo::showFromProfile(self->mPartnerID, - self->getScreenRect()); + self->calcScreenRect()); } } @@ -713,19 +713,19 @@ void LLPanelAvatarWeb::processProperties(void* data, EAvatarProcessorType type) BOOL LLPanelAvatarClassified::postBuild(void) { - childSetAction("New...",onClickNew,NULL); - childSetAction("Delete...",onClickDelete,NULL); + childSetAction("New...",onClickNew,this); + childSetAction("Delete...",onClickDelete,this); return TRUE; } BOOL LLPanelAvatarPicks::postBuild(void) { - childSetAction("New...",onClickNew,NULL); - childSetAction("Delete...",onClickDelete,NULL); + childSetAction("New...",onClickNew,this); + childSetAction("Delete...",onClickDelete,this); //For pick import and export - RK - childSetAction("Import...",onClickImport,NULL); - childSetAction("Export...",onClickExport,NULL); + childSetAction("Import...",onClickImport,this); + childSetAction("Export...",onClickExport,this); return TRUE; } diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 9625cfcdc..5fe634eed 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -255,14 +255,14 @@ BOOL LLPanelClassified::postBuild() mNameEditor = getChild("given_name_editor"); mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN); mNameEditor->setCommitOnFocusLost(TRUE); - mNameEditor->setFocusReceivedCallback(focusReceived, this); + mNameEditor->setFocusReceivedCallback(boost::bind(focusReceived, _1, this)); mNameEditor->setCommitCallback(onCommitAny); mNameEditor->setCallbackUserData(this); mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII ); mDescEditor = getChild("desc_editor"); mDescEditor->setCommitOnFocusLost(TRUE); - mDescEditor->setFocusReceivedCallback(focusReceived, this); + mDescEditor->setFocusReceivedCallback(boost::bind(focusReceived, _1, this)); mDescEditor->setCommitCallback(onCommitAny); mDescEditor->setCallbackUserData(this); mDescEditor->setTabsToNextField(TRUE); @@ -270,22 +270,18 @@ BOOL LLPanelClassified::postBuild() mLocationEditor = getChild("location_editor"); mSetBtn = getChild( "set_location_btn"); - mSetBtn->setClickedCallback(onClickSet); - mSetBtn->setCallbackUserData(this); + mSetBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickSet, this)); mTeleportBtn = getChild( "classified_teleport_btn"); - mTeleportBtn->setClickedCallback(onClickTeleport); - mTeleportBtn->setCallbackUserData(this); + mTeleportBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickTeleport, this)); mMapBtn = getChild( "classified_map_btn"); - mMapBtn->setClickedCallback(onClickMap); - mMapBtn->setCallbackUserData(this); + mMapBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickMap, this)); if(mInFinder) { mProfileBtn = getChild( "classified_profile_btn"); - mProfileBtn->setClickedCallback(onClickProfile); - mProfileBtn->setCallbackUserData(this); + mProfileBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickProfile, this)); } mCategoryCombo = getChild( "classified_category_combo"); @@ -319,7 +315,7 @@ BOOL LLPanelClassified::postBuild() } mUpdateBtn = getChild("classified_update_btn"); - mUpdateBtn->setClickedCallback(onClickUpdate); + mUpdateBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickUpdate, this)); mUpdateBtn->setCallbackUserData(this); if (!mInFinder) diff --git a/indra/newview/llpanelcontents.cpp b/indra/newview/llpanelcontents.cpp index a049c58ac..e5219fd99 100644 --- a/indra/newview/llpanelcontents.cpp +++ b/indra/newview/llpanelcontents.cpp @@ -68,7 +68,7 @@ #include "lltool.h" #include "lltoolmgr.h" #include "lltoolcomp.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" // [RLVa:KB] - Checked: 2010-03-31 (RLVa-1.2.0c) #include "rlvhandler.h" // [/RLVa:KB] diff --git a/indra/newview/llpanelcontents.h b/indra/newview/llpanelcontents.h index ea0670749..b7b103d5b 100644 --- a/indra/newview/llpanelcontents.h +++ b/indra/newview/llpanelcontents.h @@ -37,7 +37,7 @@ #include "llpanel.h" class LLButton; -class LLPanelInventory; +class LLPanelObjectInventory; class LLViewerObject; class LLCheckBoxCtrl; class LLSpinCtrl; @@ -58,7 +58,7 @@ protected: void getState(LLViewerObject *object); public: - LLPanelInventory* mPanelInventory; + LLPanelObjectInventory* mPanelInventory; }; #endif diff --git a/indra/newview/llpanelevent.cpp b/indra/newview/llpanelevent.cpp index 0105180a4..9b453fbeb 100644 --- a/indra/newview/llpanelevent.cpp +++ b/indra/newview/llpanelevent.cpp @@ -89,20 +89,16 @@ BOOL LLPanelEvent::postBuild() mTBCover = getChild("event_cover"); mTeleportBtn = getChild( "teleport_btn"); - mTeleportBtn->setClickedCallback(onClickTeleport); - mTeleportBtn->setCallbackUserData(this); + mTeleportBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickTeleport,this)); mMapBtn = getChild( "map_btn"); - mMapBtn->setClickedCallback(onClickMap); - mMapBtn->setCallbackUserData(this); + mMapBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickMap,this)); mNotifyBtn = getChild( "notify_btn"); - mNotifyBtn->setClickedCallback(onClickNotify); - mNotifyBtn->setCallbackUserData(this); + mNotifyBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickNotify,this)); mCreateEventBtn = getChild( "create_event_btn"); - mCreateEventBtn->setClickedCallback(onClickCreateEvent); - mCreateEventBtn->setCallbackUserData(this); + mCreateEventBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickCreateEvent,this)); return TRUE; } diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 835d8e514..b1e55d30c 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -72,8 +72,7 @@ BOOL LLPanelGroupTab::postBuild() LLButton* button = getChild("help_button"); if (button) { - button->setClickedCallback(onClickHelp); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&LLPanelGroupTab::onClickHelp,this)); } mHelpText = getString("help_text"); @@ -267,23 +266,21 @@ BOOL LLPanelGroup::postBuild() LLButton* button = getChild("btn_ok"); if (button) { - button->setClickedCallback(onBtnOK); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnOK,this)); button->setVisible(mAllowEdit); } button = getChild("btn_cancel"); if (button) { - button->setClickedCallback(onBtnCancel); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnCancel,this)); button->setVisible(mAllowEdit); } button = getChild("btn_apply"); if (button) { - button->setClickedCallback(onBtnApply); + button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnApply,this)); button->setVisible(mAllowEdit); button->setEnabled(FALSE); @@ -293,8 +290,7 @@ BOOL LLPanelGroup::postBuild() button = getChild("btn_refresh"); if (button) { - button->setClickedCallback(onBtnRefresh); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnRefresh,this)); button->setVisible(mAllowEdit); } diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index d608f7a98..5aecdce57 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -127,37 +127,36 @@ BOOL LLPanelGroupGeneral::postBuild() if(mEditCharter) { mEditCharter->setCommitCallback(onCommitAny); - mEditCharter->setFocusReceivedCallback(onFocusEdit, this); - mEditCharter->setFocusChangedCallback(onFocusEdit, this); + mEditCharter->setFocusReceivedCallback(boost::bind(&LLPanelGroupGeneral::onFocusEdit, this)); + mEditCharter->setFocusChangedCallback(boost::bind(&LLPanelGroupGeneral::onFocusEdit, this)); mEditCharter->setCallbackUserData(this); } mBtnJoinGroup = getChild("join_button", recurse); if ( mBtnJoinGroup ) { - mBtnJoinGroup->setClickedCallback(onClickJoin); - mBtnJoinGroup->setCallbackUserData(this); + mBtnJoinGroup->setClickedCallback(boost::bind(&LLPanelGroupGeneral::onClickJoin, this)); } mBtnInfo = getChild("info_button", recurse); if ( mBtnInfo ) { - mBtnInfo->setClickedCallback(onClickInfo); - mBtnInfo->setCallbackUserData(this); + mBtnInfo->setClickedCallback(boost::bind(&LLPanelGroupGeneral::onClickInfo, this)); } LLTextBox* founder = getChild("founder_name"); if (founder) { mFounderName = new LLNameBox(founder->getName(),founder->getRect(),LLUUID::null,FALSE,founder->getFont(),founder->getMouseOpaque()); - removeChild(founder, TRUE); + removeChild(founder); + delete founder; addChild(mFounderName); } mListVisibleMembers = getChild("visible_members", recurse); if (mListVisibleMembers) { - mListVisibleMembers->setDoubleClickCallback(openProfile); + mListVisibleMembers->setDoubleClickCallback(&LLPanelGroupGeneral::openProfile); mListVisibleMembers->setCallbackUserData(this); } @@ -165,7 +164,7 @@ BOOL LLPanelGroupGeneral::postBuild() mCtrlShowInGroupList = getChild("show_in_group_list", recurse); if (mCtrlShowInGroupList) { - mCtrlShowInGroupList->setCommitCallback(onCommitAny); + mCtrlShowInGroupList->setCommitCallback(&LLPanelGroupGeneral::onCommitAny); mCtrlShowInGroupList->setCallbackUserData(this); } @@ -279,15 +278,12 @@ BOOL LLPanelGroupGeneral::postBuild() return LLPanelGroupTab::postBuild(); } -// static -void LLPanelGroupGeneral::onFocusEdit(LLFocusableElement* ctrl, void* data) +void LLPanelGroupGeneral::onFocusEdit() { - LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; - self->updateChanged(); - self->notifyObservers(); + updateChanged(); + notifyObservers(); } -// static void LLPanelGroupGeneral::onCommitAny(LLUICtrl* ctrl, void* data) { LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index c4572e2d4..2eeb901c7 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -67,7 +67,7 @@ public: virtual void draw(); private: - static void onFocusEdit(LLFocusableElement* ctrl, void* data); + void onFocusEdit(); static void onCommitAny(LLUICtrl* ctrl, void* data); static void onCommitUserOnly(LLUICtrl* ctrl, void* data); static void onCommitTitle(LLUICtrl* ctrl, void* data); diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index cff4637bd..bd8cf5551 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -527,17 +527,14 @@ BOOL LLPanelGroupInvite::postBuild() { // default to opening avatarpicker automatically // (*impl::callbackClickAdd)((void*)this); - button->setClickedCallback(impl::callbackClickAdd); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&impl::callbackClickAdd, this)); } mImplementation->mRemoveButton = getChild("remove_button", recurse); if ( mImplementation->mRemoveButton ) { - mImplementation->mRemoveButton-> - setClickedCallback(impl::callbackClickRemove); - mImplementation->mRemoveButton->setCallbackUserData(mImplementation); + mImplementation->mRemoveButton->setClickedCallback(boost::bind(&impl::callbackClickRemove, mImplementation)); mImplementation->mRemoveButton->setEnabled(FALSE); } @@ -545,17 +542,14 @@ BOOL LLPanelGroupInvite::postBuild() getChild("ok_button", recurse); if ( mImplementation->mOKButton ) { - mImplementation->mOKButton-> - setClickedCallback(impl::callbackClickOK); - mImplementation->mOKButton->setCallbackUserData(mImplementation); + mImplementation->mOKButton->setClickedCallback(boost::bind(&impl::callbackClickOK, mImplementation)); mImplementation->mOKButton->setEnabled(FALSE); } button = getChild("cancel_button", recurse); if ( button ) { - button->setClickedCallback(impl::callbackClickCancel); - button->setCallbackUserData(mImplementation); + button->setClickedCallback(boost::bind(&impl::callbackClickCancel,mImplementation)); } mImplementation->mOwnerWarning = getString("confirm_invite_owner_str"); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index 474c98ec2..fa0432b6c 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -654,7 +654,7 @@ BOOL LLPanelGroupLandMoney::postBuild() if ( mImplementationp->mMapButtonp ) { - mImplementationp->mMapButtonp->setClickedCallback(LLPanelGroupLandMoney::impl::mapCallback, mImplementationp); + mImplementationp->mMapButtonp->setClickedCallback(boost::bind(&LLPanelGroupLandMoney::impl::mapCallback, mImplementationp)); } if ( mImplementationp->mGroupOverLimitTextp ) diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index bde9aa848..13786d9e4 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -220,13 +220,11 @@ BOOL LLPanelGroupNotices::postBuild() mNoticesList->setCallbackUserData(this); mBtnNewMessage = getChild("create_new_notice",recurse); - mBtnNewMessage->setClickedCallback(onClickNewMessage); - mBtnNewMessage->setCallbackUserData(this); + mBtnNewMessage->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickNewMessage,this)); mBtnNewMessage->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_NOTICES_SEND)); mBtnGetPastNotices = getChild("refresh_notices",recurse); - mBtnGetPastNotices->setClickedCallback(onClickRefreshNotices); - mBtnGetPastNotices->setCallbackUserData(this); + mBtnGetPastNotices->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickRefreshNotices,this)); // Create mCreateSubject = getChild("create_subject",recurse); @@ -240,12 +238,10 @@ BOOL LLPanelGroupNotices::postBuild() mCreateInventoryIcon->setVisible(FALSE); mBtnSendMessage = getChild("send_notice",recurse); - mBtnSendMessage->setClickedCallback(onClickSendMessage); - mBtnSendMessage->setCallbackUserData(this); + mBtnSendMessage->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickSendMessage,this)); mBtnRemoveAttachment = getChild("remove_attachment",recurse); - mBtnRemoveAttachment->setClickedCallback(onClickRemoveAttachment); - mBtnRemoveAttachment->setCallbackUserData(this); + mBtnRemoveAttachment->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickRemoveAttachment,this)); mBtnRemoveAttachment->setEnabled(FALSE); // View @@ -261,8 +257,7 @@ BOOL LLPanelGroupNotices::postBuild() mViewInventoryIcon->setVisible(FALSE); mBtnOpenAttachment = getChild("open_attachment",recurse); - mBtnOpenAttachment->setClickedCallback(onClickOpenAttachment); - mBtnOpenAttachment->setCallbackUserData(this); + mBtnOpenAttachment->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickOpenAttachment,this)); mNoNoticesStr = getString("no_notices_text"); @@ -278,7 +273,8 @@ BOOL LLPanelGroupNotices::postBuild() target->setToolTip(dtv->getToolTip()); mPanelCreateNotice->addChild(target); - mPanelCreateNotice->removeChild(dtv, TRUE); + mPanelCreateNotice->removeChild(dtv); + delete dtv; arrangeNoticeView(VIEW_PAST_NOTICE); diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index be41b0f87..cf07b5edb 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -501,15 +501,13 @@ BOOL LLPanelGroupSubTab::postBuild() mSearchButton = getChild("search_button", recurse); if (!mSearchButton) return FALSE; - mSearchButton->setClickedCallback(onClickSearch); - mSearchButton->setCallbackUserData(this); + mSearchButton->setClickedCallback(boost::bind(&LLPanelGroupSubTab::onClickSearch,this)); mSearchButton->setEnabled(FALSE); mShowAllButton = getChild("show_all_button", recurse); if (!mShowAllButton) return FALSE; - mShowAllButton->setClickedCallback(onClickShowAll); - mShowAllButton->setCallbackUserData(this); + mShowAllButton->setClickedCallback(boost::bind(&LLPanelGroupSubTab::onClickShowAll,this)); mShowAllButton->setEnabled(FALSE); // Get icons for later use. @@ -521,21 +519,24 @@ BOOL LLPanelGroupSubTab::postBuild() if (icon && !icon->getImageName().empty()) { mActionIcons["folder"] = icon->getImageName(); - removeChild(icon, TRUE); + removeChild(icon); + delete icon; } icon = getChild("power_all_have_icon",no_recurse); if (icon && !icon->getImageName().empty()) { mActionIcons["full"] = icon->getImageName(); - removeChild(icon, TRUE); + removeChild(icon); + delete icon; } icon = getChild("power_partial_icon",no_recurse); if (icon && !icon->getImageName().empty()) { mActionIcons["partial"] = icon->getImageName(); - removeChild(icon, TRUE); + removeChild(icon); + delete icon; } return LLPanelGroupTab::postBuild(); @@ -662,8 +663,7 @@ bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action) void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, - icon_map_t& icons, - void (*commit_callback)(LLUICtrl*,void*), + LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, BOOL is_owner_role) @@ -683,7 +683,6 @@ void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, allowed_by_some, allowed_by_all, (*ras_it), - icons, commit_callback, show_all, filter, @@ -695,8 +694,7 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, LLRoleActionSet* action_set, - icon_map_t& icons, - void (*commit_callback)(LLUICtrl*,void*), + LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, BOOL is_owner_role) @@ -709,10 +707,11 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, LLSD row; row["columns"][0]["column"] = "icon"; - icon_map_t::iterator iter = icons.find("folder"); - if (iter != icons.end()) + row["columns"][0]["type"] = "icon"; + + icon_map_t::iterator iter = mActionIcons.find("folder"); + if (iter != mActionIcons.end()) { - row["columns"][0]["type"] = "icon"; row["columns"][0]["value"] = (*iter).second; } @@ -774,8 +773,8 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, { if (show_full_strength) { - icon_map_t::iterator iter = icons.find("full"); - if (iter != icons.end()) + icon_map_t::iterator iter = mActionIcons.find("full"); + if (iter != mActionIcons.end()) { row["columns"][column_index]["column"] = "checkbox"; row["columns"][column_index]["type"] = "icon"; @@ -785,8 +784,8 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, } else { - icon_map_t::iterator iter = icons.find("partial"); - if (iter != icons.end()) + icon_map_t::iterator iter = mActionIcons.find("partial"); + if (iter != mActionIcons.end()) { row["columns"][column_index]["column"] = "checkbox"; row["columns"][column_index]["type"] = "icon"; @@ -810,7 +809,6 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, LLCheckBoxCtrl* check = check_cell->getCheckBox(); check->setEnabled(can_change_actions); check->setCommitCallback(commit_callback); - check->setCallbackUserData(ctrl->getCallbackUserData()); check->setToolTip( check->getLabel() ); if (show_all) @@ -909,16 +907,14 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) LLButton* button = parent->getChild("member_invite", recurse); if ( button ) { - button->setClickedCallback(onInviteMember); - button->setCallbackUserData(this); + button->setClickedCallback(boost::bind(&LLPanelGroupMembersSubTab::onInviteMember,this)); button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE)); } mEjectBtn = parent->getChild("member_eject", recurse); if ( mEjectBtn ) { - mEjectBtn->setClickedCallback(onEjectMembers); - mEjectBtn->setCallbackUserData(this); + mEjectBtn->setClickedCallback(boost::bind(&LLPanelGroupMembersSubTab::onEjectMembers,this)); mEjectBtn->setEnabled(FALSE); } @@ -953,7 +949,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() if (selection.empty()) return; // Build a vector of all selected members, and gather allowed actions. - std::vector selected_members; + uuid_vec_t selected_members; U64 allowed_by_all = 0xffffffffffffLL; U64 allowed_by_some = 0; @@ -961,10 +957,12 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() for (itor = selection.begin(); itor != selection.end(); ++itor) { - selected_members.push_back( (*itor)->getUUID() ); + LLUUID member_id = (*itor)->getUUID(); + + selected_members.push_back( member_id ); // Get this member's power mask including any unsaved changes - U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID()); + U64 powers = getAgentPowersBasedOnRoleChanges( member_id ); allowed_by_all &= powers; allowed_by_some |= powers; @@ -977,7 +975,6 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() buildActionsList(mAllowedActionsList, allowed_by_some, allowed_by_all, - mActionIcons, NULL, FALSE, FALSE, @@ -1018,8 +1015,8 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() if (cb_enable && (count > 0) && role_id == gdatap->mOwnerRole) { // Check if any owners besides this agent are selected. - std::vector::const_iterator member_iter; - std::vector::const_iterator member_end = + uuid_vec_t::const_iterator member_iter; + uuid_vec_t::const_iterator member_end = selected_members.end(); for (member_iter = selected_members.begin(); member_iter != member_end; @@ -1045,7 +1042,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() //now see if there are any role changes for the selected //members and remember to include them - std::vector::iterator sel_mem_iter = selected_members.begin(); + uuid_vec_t::iterator sel_mem_iter = selected_members.begin(); for (; sel_mem_iter != selected_members.end(); sel_mem_iter++) { LLRoleMemberChangeType type; @@ -1103,7 +1100,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() check->setTentative( (0 != count) && (selected_members.size() != - (std::vector::size_type)count)); + (uuid_vec_t::size_type)count)); //NOTE: as of right now a user can break the group //by removing himself from a role if he is the @@ -1278,7 +1275,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id, buildActionsList(mAllowedActionsList, powers_some_have, powers_all_have, - mActionIcons, NULL, FALSE, FALSE, @@ -1775,8 +1771,7 @@ BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root) parent->getChild("role_create", recurse); if ( mCreateRoleButton ) { - mCreateRoleButton->setCallbackUserData(this); - mCreateRoleButton->setClickedCallback(onCreateRole); + mCreateRoleButton->setClickedCallback(boost::bind(&LLPanelGroupRolesSubTab::onCreateRole,this)); mCreateRoleButton->setEnabled(FALSE); } @@ -1784,8 +1779,7 @@ BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root) parent->getChild("role_delete", recurse); if ( mDeleteRoleButton ) { - mDeleteRoleButton->setCallbackUserData(this); - mDeleteRoleButton->setClickedCallback(onDeleteRole); + mDeleteRoleButton->setClickedCallback(boost::bind(&LLPanelGroupRolesSubTab::onDeleteRole,this)); mDeleteRoleButton->setEnabled(FALSE); } @@ -1810,7 +1804,7 @@ BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root) mRoleDescription->setCommitOnFocusLost(TRUE); mRoleDescription->setCallbackUserData(this); mRoleDescription->setCommitCallback(onDescriptionCommit); - mRoleDescription->setFocusReceivedCallback(onDescriptionFocus, this); + mRoleDescription->setFocusReceivedCallback(boost::bind(&LLPanelGroupRolesSubTab::onDescriptionFocus, this)); setFooterEnabled(FALSE); @@ -2059,8 +2053,7 @@ void LLPanelGroupRolesSubTab::handleRoleSelect() buildActionsList(mAllowedActionsList, rd.mRolePowers, 0LL, - mActionIcons, - onActionCheck, + boost::bind(&LLPanelGroupRolesSubTab::handleActionCheck, this, _1, false), TRUE, FALSE, is_owner_role); @@ -2157,24 +2150,18 @@ void LLPanelGroupRolesSubTab::buildMembersList() } } -// static -void LLPanelGroupRolesSubTab::onActionCheck(LLUICtrl* ctrl, void* user_data) -{ - LLPanelGroupRolesSubTab* self = static_cast(user_data); - LLCheckBoxCtrl* check = static_cast(ctrl); - if (!check || !self) return; - - self->handleActionCheck(check); -} - struct ActionCBData { LLPanelGroupRolesSubTab* mSelf; LLCheckBoxCtrl* mCheck; }; -void LLPanelGroupRolesSubTab::handleActionCheck(LLCheckBoxCtrl* check, bool force) +void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force) { + LLCheckBoxCtrl* check = dynamic_cast(ctrl); + if (!check) + return; + lldebugs << "LLPanelGroupRolesSubTab::handleActionSelect()" << llendl; LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); @@ -2273,14 +2260,10 @@ void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_dat self->notifyObservers(); } -// static -void LLPanelGroupRolesSubTab::onDescriptionFocus(LLFocusableElement* ctrl, void* user_data) +void LLPanelGroupRolesSubTab::onDescriptionFocus() { - LLPanelGroupRolesSubTab* self = static_cast(user_data); - if (!self) return; - - self->mHasRoleChange = TRUE; - self->notifyObservers(); + mHasRoleChange = TRUE; + notifyObservers(); } // static @@ -2474,7 +2457,7 @@ BOOL LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root) mActionList->setCallbackUserData(this); mActionList->setCommitOnSelectionChange(TRUE); - mActionList->setCommitCallback(onActionSelect); + mActionList->setCommitCallback(boost::bind(&LLPanelGroupActionsSubTab::handleActionSelect, this)); mActionMembers->setCallbackUserData(this); mActionRoles->setCallbackUserData(this); @@ -2530,20 +2513,12 @@ void LLPanelGroupActionsSubTab::update(LLGroupChange gc) buildActionsList(mActionList, GP_ALL_POWERS, GP_ALL_POWERS, - mActionIcons, NULL, FALSE, TRUE, FALSE); } -// static -void LLPanelGroupActionsSubTab::onActionSelect(LLUICtrl* scroll, void* data) -{ - LLPanelGroupActionsSubTab* self = static_cast(data); - self->handleActionSelect(); -} - void LLPanelGroupActionsSubTab::handleActionSelect() { mActionMembers->deleteAllItems(); diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 95057bbfa..3083c2d56 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -130,8 +130,7 @@ public: void buildActionsList(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, - icon_map_t& icons, - void (*commit_callback)(LLUICtrl*,void*), + LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, BOOL is_owner_role); @@ -139,8 +138,7 @@ public: U64 allowed_by_some, U64 allowed_by_all, LLRoleActionSet* action_set, - icon_map_t& icons, - void (*commit_callback)(LLUICtrl*,void*), + LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, BOOL is_owner_role); @@ -248,14 +246,12 @@ public: void handleRoleSelect(); void buildMembersList(); - static void onActionCheck(LLUICtrl*, void*); - void handleActionCheck(LLCheckBoxCtrl*, bool force=false); bool addActionCB(const LLSD& notification, const LLSD& response, LLCheckBoxCtrl* check); static void onPropertiesKey(LLLineEditor*, void*); static void onDescriptionCommit(LLUICtrl*, void*); - static void onDescriptionFocus(LLFocusableElement*, void*); + void onDescriptionFocus(); static void onMemberVisibilityChange(LLUICtrl*, void*); void handleMemberVisibilityChange(bool value); @@ -268,10 +264,8 @@ public: void saveRoleChanges(); protected: - LLSD createRoleItem(const LLUUID& role_id, - std::string name, - std::string title, - S32 members); + void handleActionCheck(LLUICtrl* ctrl, bool force); + LLSD createRoleItem(const LLUUID& role_id, std::string name, std::string title, S32 members); LLScrollListCtrl* mRolesList; LLNameListCtrl* mAssignedMembersList; @@ -306,7 +300,6 @@ public: virtual bool apply(std::string& mesg); virtual void update(LLGroupChange gc); - static void onActionSelect(LLUICtrl*, void*); void handleActionSelect(); protected: LLScrollListCtrl* mActionList; diff --git a/indra/newview/llpanelgroupvoting.cpp b/indra/newview/llpanelgroupvoting.cpp index d3d24b7d0..b4bee1522 100644 --- a/indra/newview/llpanelgroupvoting.cpp +++ b/indra/newview/llpanelgroupvoting.cpp @@ -1575,35 +1575,25 @@ BOOL LLPanelGroupVoting::postBuild() mImpl->mVotesHistory->setDoubleClickCallback(impl::onDoubleClickHistoryItem); mImpl->mVotesHistory->setCallbackUserData(mImpl); - mImpl->mBtnAbstain->setClickedCallback(impl::onClickAbstain); - mImpl->mBtnAbstain->setCallbackUserData(mImpl); + mImpl->mBtnAbstain->setClickedCallback(boost::bind(&impl::onClickAbstain,mImpl)); - mImpl->mBtnNo->setClickedCallback(impl::onClickNo); - mImpl->mBtnNo->setCallbackUserData(mImpl); + mImpl->mBtnNo->setClickedCallback(boost::bind(&impl::onClickNo,mImpl)); - mImpl->mBtnYes->setClickedCallback(impl::onClickYes); - mImpl->mBtnYes->setCallbackUserData(mImpl); + mImpl->mBtnYes->setClickedCallback(boost::bind(&impl::onClickYes,mImpl)); - mImpl->mBtnCreateProposal->setClickedCallback(impl::onClickCreateProposal); - mImpl->mBtnCreateProposal->setCallbackUserData(mImpl); + mImpl->mBtnCreateProposal->setClickedCallback(boost::bind(&impl::onClickCreateProposal,mImpl)); - mImpl->mBtnSubmitProposal->setClickedCallback(impl::onClickSubmitProposal); - mImpl->mBtnSubmitProposal->setCallbackUserData(mImpl); + mImpl->mBtnSubmitProposal->setClickedCallback(boost::bind(&impl::onClickSubmitProposal,mImpl)); - mImpl->mBtnCancelProposal->setClickedCallback(impl::onClickCancelProposal); - mImpl->mBtnCancelProposal->setCallbackUserData(mImpl); + mImpl->mBtnCancelProposal->setClickedCallback(boost::bind(&impl::onClickCancelProposal,mImpl)); - mImpl->mBtnViewProposalList->setClickedCallback(impl::onClickViewProposalList); - mImpl->mBtnViewProposalList->setCallbackUserData(mImpl); + mImpl->mBtnViewProposalList->setClickedCallback(boost::bind(&impl::onClickViewProposalList,mImpl)); - mImpl->mBtnViewProposalItem->setClickedCallback(impl::onClickViewProposalItem); - mImpl->mBtnViewProposalItem->setCallbackUserData(mImpl); + mImpl->mBtnViewProposalItem->setClickedCallback(boost::bind(&impl::onClickViewProposalItem,mImpl)); - mImpl->mBtnViewHistoryList->setClickedCallback(impl::onClickViewHistoryList); - mImpl->mBtnViewHistoryList->setCallbackUserData(mImpl); + mImpl->mBtnViewHistoryList->setClickedCallback(boost::bind(&impl::onClickViewHistoryList,mImpl)); - mImpl->mBtnViewHistoryItem->setClickedCallback(impl::onClickViewHistoryItem); - mImpl->mBtnViewHistoryItem->setCallbackUserData(mImpl); + mImpl->mBtnViewHistoryItem->setClickedCallback(boost::bind(&impl::onClickViewHistoryItem,mImpl)); gMessageSystem->setHandlerFuncFast(_PREHASH_GroupActiveProposalItemReply, impl::processGroupActiveProposalItemReply); diff --git a/indra/newview/llpanelinventory.h b/indra/newview/llpanelinventory.h deleted file mode 100644 index d06d716aa..000000000 --- a/indra/newview/llpanelinventory.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @file llpanelinventory.h - * @brief LLPanelInventory class definition - * - * $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$ - */ - -// ***************************************************************************** -// -// This class represents the panel used to view and control a -// particular task's inventory. -// -// ***************************************************************************** - -#ifndef LL_LLPANELINVENTORY_H -#define LL_LLPANELINVENTORY_H - -#include "llinventory.h" -#include "lluuid.h" -#include "llmap.h" -#include "llviewerobject.h" -#include "llvoinventorylistener.h" -#include "llpanel.h" - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLPanelInventory -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLScrollableContainerView; -class LLFolderView; -class LLFolderViewFolder; -class LLViewerObject; -class LLFolderViewEventListener; -//class LLVOInventoryListener; - -// Utility function to hide all entries except those in the list -class LLMenuGL; -void hide_context_entries(LLMenuGL& menu, - const std::vector &entries_to_show, - const std::vector &disabled_entries); - -class LLPanelInventory : public LLPanel, public LLVOInventoryListener -{ -protected: - LLScrollableContainerView* mScroller; - LLFolderView* mFolders; - - LLUUID mTaskUUID; - BOOL mHaveInventory; - BOOL mIsInventoryEmpty; - BOOL mInventoryNeedsUpdate; - -protected: - void reset(); - /*virtual*/ void inventoryChanged(LLViewerObject* object, - LLInventoryObject::object_list_t* inventory, - S32 serial_num, - void* user_data); - void updateInventory(); - void createFolderViews(LLInventoryObject* inventory_root, LLInventoryObject::object_list_t& contents); - void createViewsForCategory(LLInventoryObject::object_list_t* inventory, - LLInventoryObject* parent, - LLFolderViewFolder* folder); - - void clearContents(); - -public: - LLPanelInventory(const std::string& name, const LLRect& rect); - virtual ~LLPanelInventory(); - - void refresh(); - const LLUUID& getTaskUUID() { return mTaskUUID;} - void removeSelectedItem(); - void startRenamingSelectedItem(); - - LLFolderView* getRootFolder() const { return mFolders; } - - virtual void draw(); - virtual void deleteAllChildren(); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg); - - static void idle(void* user_data); -}; - -void init_object_inventory_panel_actions(LLPanelInventory *panel); - -#endif // LL_LLPANELINVENTORY_H diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 79e30b8f9..045b722a4 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -247,7 +247,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLPanelLogin::sInstance = this; // add to front so we are the bottom-most child - gViewerWindow->getRootView()->addChildAtEnd(this); + gViewerWindow->getRootView()->addChildInBack(this); // Logo mLogoImage = LLUI::getUIImage("startup_logo.j2c"); @@ -263,7 +263,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, #if !USE_VIEWER_AUTH LLComboBox* name_combo = sInstance->getChild("name_combo"); name_combo->setCommitCallback(onSelectLoginEntry); - name_combo->setFocusLostCallback(onLoginComboLostFocus); + name_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLoginComboLostFocus, this, name_combo)); name_combo->setPrevalidate(LLLineEditor::prevalidatePrintableNotPipe); name_combo->setSuppressTentative(true); name_combo->setSuppressAutoComplete(true); @@ -1245,17 +1245,12 @@ void LLPanelLogin::onSelectLoginEntry(LLUICtrl* ctrl, void* data) } } -// static -void LLPanelLogin::onLoginComboLostFocus(LLFocusableElement* fe, void*) +void LLPanelLogin::onLoginComboLostFocus(LLComboBox* combo_box) { - if (sInstance) + if(combo_box->isTextDirty()) { - LLComboBox* combo = sInstance->getChild("name_combo"); - if(fe == combo && combo->isTextDirty()) - { - clearPassword(); - combo->resetTextDirty(); - } + clearPassword(); + combo_box->resetTextDirty(); } } diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 3c4887b85..3fd697b34 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -39,6 +39,7 @@ #include "llsavedlogins.h" class LLUIImage; +class LLComboBox; // extern std::string gFullName; @@ -118,7 +119,7 @@ private: //static void onSelectServer(LLUICtrl*, void*); //static void onServerComboLostFocus(LLFocusableElement*, void*); static void onSelectLoginEntry(LLUICtrl*, void*); - static void onLoginComboLostFocus(LLFocusableElement* fe, void*); + void onLoginComboLostFocus(LLComboBox* combo_box); static void onNameCheckChanged(LLUICtrl* ctrl, void* data); static void clearPassword(); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index a844f8a78..ac746097b 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -42,7 +42,7 @@ #include "lltooldraganddrop.h" #include "llviewermenu.h" #include "llviewertexturelist.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llappviewer.h" #include "rlvhandler.h" @@ -71,7 +71,6 @@ public: BOOL getCheckSinceLogoff(); static void onTimeAgo(LLUICtrl*, void *); - static void onCheckSinceLogoff(LLUICtrl*, void *); static void onCloseBtn(void* user_data); static void selectAllTypes(void* user_data); static void selectNoTypes(void* user_data); @@ -152,7 +151,8 @@ BOOL LLInventoryView::postBuild() mActivePanel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); mActivePanel->getFilter()->markDefault(); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - mActivePanel->setSelectCallback(onSelectionChange, mActivePanel); + mActivePanel->setSelectCallback(boost::bind(&LLInventoryView::onSelectionChange, this, mActivePanel, _1, _2)); + mResortActivePanel = true; } LLInventoryPanel* recent_items_panel = getChild("Recent Items"); if (recent_items_panel) @@ -161,7 +161,7 @@ BOOL LLInventoryView::postBuild() recent_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::RECENTITEMS_SORT_ORDER)); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); recent_items_panel->getFilter()->markDefault(); - recent_items_panel->setSelectCallback(onSelectionChange, recent_items_panel); + recent_items_panel->setSelectCallback(boost::bind(&LLInventoryView::onSelectionChange, this, recent_items_panel, _1, _2)); } LLInventoryPanel* worn_items_panel = getChild("Worn Items"); if (worn_items_panel) @@ -170,7 +170,8 @@ BOOL LLInventoryView::postBuild() worn_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); worn_items_panel->getFilter()->markDefault(); worn_items_panel->setFilterWorn(true); - worn_items_panel->setSelectCallback(onSelectionChange, worn_items_panel); + worn_items_panel->setFilterLinks(LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); + worn_items_panel->setSelectCallback(boost::bind(&LLInventoryView::onSelectionChange, this, worn_items_panel, _1, _2)); } // Now load the stored settings from disk, if available. @@ -221,8 +222,6 @@ BOOL LLInventoryView::postBuild() childSetAction("Inventory.ResetAll",onResetAll,this); childSetAction("Inventory.ExpandAll",onExpandAll,this); childSetAction("collapse_btn", onCollapseAll, this); - - //panel->getFilter()->markDefault(); return TRUE; } @@ -349,12 +348,12 @@ BOOL LLInventoryView::handleKeyHere(KEY key, MASK mask) && mask == MASK_NONE) { // move focus to inventory proper - root_folder->setFocus(TRUE); + mActivePanel->setFocus(TRUE); root_folder->scrollToShowSelection(); return TRUE; } - if (root_folder->hasFocus() && key == KEY_UP) + if (mActivePanel->hasFocus() && key == KEY_UP) { startSearch(); } @@ -544,6 +543,7 @@ void LLInventoryView::onClearSearch(void* user_data) self->mActivePanel->getRootFolder()->applyFunctorRecursively(opener); self->mActivePanel->getRootFolder()->scrollToShowSelection(); } + //self->mFilterSubString = ""; } //static @@ -561,10 +561,11 @@ void LLInventoryView::onSearchEdit(const std::string& search_string, void* user_ LLInventoryModelBackgroundFetch::instance().start(); + //self->mFilterSubString = search_string; std::string filter_text = search_string; std::string uppercase_search_string = filter_text; LLStringUtil::toUpper(uppercase_search_string); - if (self->mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty()) + if (self->mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty() /*self->mFilterSubString.empty()*/) { // current filter and new filter empty, do nothing return; @@ -578,82 +579,57 @@ void LLInventoryView::onSearchEdit(const std::string& search_string, void* user_ } // set new filter string - self->mActivePanel->setFilterSubString(uppercase_search_string); + self->mActivePanel->setFilterSubString(uppercase_search_string/*self->mFilterSubString*/); } +struct FilterEntry : public LLDictionaryEntry +{ + FilterEntry(const std::string &filter_name) : + LLDictionaryEntry(filter_name){} +}; + +class LLFilterDictionary : public LLSingleton, + public LLDictionary +{ +public: + LLFilterDictionary() + {} + void init(LLInventoryView *view) + { + addEntry(0x1 << LLInventoryType::IT_ANIMATION, new FilterEntry(view->getString("filter_type_animation"))); + addEntry(0x1 << LLInventoryType::IT_CALLINGCARD, new FilterEntry(view->getString("filter_type_callingcard"))); + addEntry(0x1 << LLInventoryType::IT_WEARABLE, new FilterEntry(view->getString("filter_type_wearable"))); + addEntry(0x1 << LLInventoryType::IT_GESTURE, new FilterEntry(view->getString("filter_type_gesture"))); + addEntry(0x1 << LLInventoryType::IT_LANDMARK, new FilterEntry(view->getString("filter_type_landmark"))); + addEntry(0x1 << LLInventoryType::IT_NOTECARD, new FilterEntry(view->getString("filter_type_notecard"))); + addEntry(0x1 << LLInventoryType::IT_OBJECT, new FilterEntry(view->getString("filter_type_object"))); + addEntry(0x1 << LLInventoryType::IT_LSL, new FilterEntry(view->getString("filter_type_script"))); + addEntry(0x1 << LLInventoryType::IT_SOUND, new FilterEntry(view->getString("filter_type_sound"))); + addEntry(0x1 << LLInventoryType::IT_TEXTURE, new FilterEntry(view->getString("filter_type_texture"))); + addEntry(0x1 << LLInventoryType::IT_SNAPSHOT, new FilterEntry(view->getString("filter_type_snapshot"))); + addEntry(0xffffffff, new FilterEntry(view->getString("filter_type_all"))); + } + virtual U32 notFound() const + { + return 0; + } +}; + //static void LLInventoryView::onQuickFilterCommit(LLUICtrl* ctrl, void* user_data) { LLComboBox* quickfilter = (LLComboBox*)ctrl; - LLInventoryView* view = (LLInventoryView*)(quickfilter->getParent()); if (!view->mActivePanel) { return; } - std::string item_type = quickfilter->getSimple(); - U32 filter_type; - if (view->getString("filter_type_animation") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_ANIMATION; - } - - else if (view->getString("filter_type_callingcard") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_CALLINGCARD; - } - - else if (view->getString("filter_type_wearable") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_WEARABLE; - } - - else if (view->getString("filter_type_gesture") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_GESTURE; - } - - else if (view->getString("filter_type_landmark") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_LANDMARK; - } - - else if (view->getString("filter_type_notecard") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_NOTECARD; - } - - else if (view->getString("filter_type_object") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_OBJECT; - } - - else if (view->getString("filter_type_script") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_LSL; - } - - else if (view->getString("filter_type_sound") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_SOUND; - } - - else if (view->getString("filter_type_texture") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_TEXTURE; - } - - else if (view->getString("filter_type_snapshot") == item_type) - { - filter_type = 0x1 << LLInventoryType::IT_SNAPSHOT; - } - - else if (view->getString("filter_type_custom") == item_type) + if (view->getString("filter_type_custom") == item_type) { // When they select custom, show the floater then return if( !(view->filtersVisible(view)) ) @@ -662,31 +638,27 @@ void LLInventoryView::onQuickFilterCommit(LLUICtrl* ctrl, void* user_data) } return; } - - else if (view->getString("filter_type_all") == item_type) + else { - // Show all types - filter_type = 0xffffffff; + if(!LLFilterDictionary::instanceExists()) + LLFilterDictionary::instance().init(view); + + U32 filter_type = LLFilterDictionary::instance().lookup(item_type); + if(!filter_type) + { + llwarns << "Ignoring unknown filter: " << item_type << llendl; + return; + } + else + { + view->mActivePanel->setFilterTypes( filter_type ); + + // Force the filters window to update itself, if it's open. + LLFloaterInventoryFinder* finder = view->getFinder(); + if( finder ) + finder->updateElementsFromFilter(); + } } - - else - { - llwarns << "Ignoring unknown filter: " << item_type << llendl; - return; - } - - view->mActivePanel->setFilterTypes( filter_type ); - - - // Force the filters window to update itself, if it's open. - LLFloaterInventoryFinder* finder = view->getFinder(); - if( finder ) - { - finder->updateElementsFromFilter(); - } - - // llinfos << "Quick Filter: " << item_type << llendl; - } @@ -694,7 +666,6 @@ void LLInventoryView::onQuickFilterCommit(LLUICtrl* ctrl, void* user_data) //static void LLInventoryView::refreshQuickFilter(LLUICtrl* ctrl) { - LLInventoryView* view = (LLInventoryView*)(ctrl->getParent()); if (!view->mActivePanel) { @@ -707,147 +678,48 @@ void LLInventoryView::refreshQuickFilter(LLUICtrl* ctrl) return; } + U32 filter_type = view->mActivePanel->getFilterObjectTypes(); - U32 filter_type = view->mActivePanel->getFilterTypes(); - - - // Mask to extract only the bit fields we care about. - // *TODO: There's probably a cleaner way to construct this mask. - U32 filter_mask = 0; - filter_mask |= (0x1 << LLInventoryType::IT_ANIMATION); - filter_mask |= (0x1 << LLInventoryType::IT_CALLINGCARD); - filter_mask |= (0x1 << LLInventoryType::IT_WEARABLE); - filter_mask |= (0x1 << LLInventoryType::IT_GESTURE); - filter_mask |= (0x1 << LLInventoryType::IT_LANDMARK); - filter_mask |= (0x1 << LLInventoryType::IT_NOTECARD); - filter_mask |= (0x1 << LLInventoryType::IT_OBJECT); - filter_mask |= (0x1 << LLInventoryType::IT_LSL); - filter_mask |= (0x1 << LLInventoryType::IT_SOUND); - filter_mask |= (0x1 << LLInventoryType::IT_TEXTURE); - filter_mask |= (0x1 << LLInventoryType::IT_SNAPSHOT); - - - filter_type &= filter_mask; + if(!LLFilterDictionary::instanceExists()) + LLFilterDictionary::instance().init(view); + // Mask to extract only the bit fields we care about. + // *TODO: There's probably a cleaner way to construct this mask. + U32 filter_mask = 0; + for (LLFilterDictionary::const_iterator_t dictionary_iter = LLFilterDictionary::instance().map_t::begin(); + dictionary_iter != LLFilterDictionary::instance().map_t::end(); dictionary_iter++) + { + if(dictionary_iter->first != 0xffffffff) + filter_mask |= dictionary_iter->first; + } + + filter_type &= filter_mask; //llinfos << "filter_type: " << filter_type << llendl; - std::string selection; - if (filter_type == filter_mask) { selection = view->getString("filter_type_all"); } - - else if (filter_type == (0x1 << LLInventoryType::IT_ANIMATION)) - { - selection = view->getString("filter_type_animation"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_CALLINGCARD)) - { - selection = view->getString("filter_type_callingcard"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_WEARABLE)) - { - selection = view->getString("filter_type_wearable"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_GESTURE)) - { - selection = view->getString("filter_type_gesture"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_LANDMARK)) - { - selection = view->getString("filter_type_landmark"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_NOTECARD)) - { - selection = view->getString("filter_type_notecard"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_OBJECT)) - { - selection = view->getString("filter_type_object"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_LSL)) - { - selection = view->getString("filter_type_script"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_SOUND)) - { - selection = view->getString("filter_type_sound"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_TEXTURE)) - { - selection = view->getString("filter_type_texture"); - } - - else if (filter_type == (0x1 << LLInventoryType::IT_SNAPSHOT)) - { - selection = view->getString("filter_type_snapshot"); - } - else { - selection = view->getString("filter_type_custom"); + const FilterEntry *entry = LLFilterDictionary::instance().lookup(filter_type); + if(entry) + selection = entry->mName; + else + selection = view->getString("filter_type_custom"); } - // Select the chosen item by label text BOOL result = quickfilter->setSimple( (selection) ); - if( !result ) - { - llinfos << "The item didn't exist: " << selection << llendl; - } - + if( !result ) + { + llinfos << "The item didn't exist: " << selection << llendl; + } } - - -// static -// BOOL LLInventoryView::incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward) -// { -// LLInventoryView* active_view = NULL; - -// for (S32 i = 0; i < sActiveViews.count(); i++) -// { -// if (gFocusMgr.childHasKeyboardFocus(sActiveViews[i])) -// { -// active_view = sActiveViews[i]; -// break; -// } -// } - -// if (!active_view) -// { -// return FALSE; -// } - -// std::string search_string(find_text); - -// if (search_string.empty()) -// { -// return FALSE; -// } - -// if (active_view->mActivePanel && -// active_view->mActivePanel->getRootFolder()->search(first_item, search_string, backward)) -// { -// return TRUE; -// } - -// return FALSE; -// } - void LLInventoryView::onResetAll(void* userdata) { LLInventoryView* self = (LLInventoryView*) userdata; @@ -896,9 +768,6 @@ void LLInventoryView::onCollapseAll(void* userdata) void LLInventoryView::onFilterSelected(void* userdata, bool from_click) { LLInventoryView* self = (LLInventoryView*) userdata; - LLInventoryFilter* filter; - - LLFloaterInventoryFinder *finder = self->getFinder(); // Find my index self->mActivePanel = (LLInventoryPanel*)self->childGetVisibleTab("inventory filter tabs"); @@ -906,7 +775,10 @@ void LLInventoryView::onFilterSelected(void* userdata, bool from_click) { return; } - filter = self->mActivePanel->getFilter(); + + //self->setFilterSubString(self->mFilterSubString); + LLInventoryFilter* filter = self->mActivePanel->getFilter(); + LLFloaterInventoryFinder *finder = self->getFinder(); if (finder) { finder->changeFilter(filter); @@ -920,21 +792,6 @@ void LLInventoryView::onFilterSelected(void* userdata, bool from_click) self->updateSortControls(); } -// static -void LLInventoryView::onSelectionChange(const std::deque &items, BOOL user_action, void* data) -{ - LLInventoryPanel* panel = (LLInventoryPanel*)data; - LLFolderView* fv = panel->getRootFolder(); - if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename - { - fv->setNeedsAutoRename(FALSE); - if (items.size()) // new asset is visible and selected - { - fv->startRenamingSelectedItem(); - } - } -} - const std::string LLInventoryView::getFilterSubString() { return mActivePanel->getFilterSubString(); @@ -954,7 +811,7 @@ BOOL LLInventoryView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, { // Check to see if we are auto scrolling from the last frame LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel(); - BOOL needsToScroll = panel->getScrollableContainer()->needsToScroll(x, y, LLScrollableContainerView::VERTICAL); + BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y); if(mFilterTabs) { if(needsToScroll) @@ -969,6 +826,39 @@ BOOL LLInventoryView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, } void LLInventoryView::changed(U32 mask) +{ + updateItemcountText(); +} + +void LLInventoryView::draw() +{ + if (mActivePanel && mSearchEditor) + { + mSearchEditor->setText(mActivePanel->getFilterSubString()); + } + + if (mActivePanel && mQuickFilterCombo) + { + refreshQuickFilter( mQuickFilterCombo ); + } + + if (mActivePanel && mResortActivePanel) + { + // EXP-756: Force resorting of the list the first time we draw the list: + // In the case of date sorting, we don't have enough information at initialization time + // to correctly sort the folders. Later manual resort doesn't do anything as the order value is + // set correctly. The workaround is to reset the order to alphabetical (or anything) then to the correct order. + U32 order = mActivePanel->getSortOrder(); + mActivePanel->setSortOrder(LLInventoryFilter::SO_NAME); + mActivePanel->setSortOrder(order); + mResortActivePanel = false; + } + + updateItemcountText(); + LLFloater::draw(); +} + +void LLInventoryView::updateItemcountText() { std::ostringstream title; title << "Inventory"; @@ -983,33 +873,6 @@ void LLInventoryView::changed(U32 mask) setTitle(title.str()); } - -void LLInventoryView::draw() -{ - if (LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) - { - LLLocale locale(LLLocale::USER_LOCALE); - std::ostringstream title; - title << "Inventory"; - std::string item_count_string; - LLResMgr::getInstance()->getIntegerString(item_count_string, gInventory.getItemCount()); - title << " (" << item_count_string << " items)"; - title << mFilterText; - setTitle(title.str()); - } - if (mActivePanel && mSearchEditor) - { - mSearchEditor->setText(mActivePanel->getFilterSubString()); - } - - if (mActivePanel && mQuickFilterCombo) - { - refreshQuickFilter( mQuickFilterCombo ); - } - - LLFloater::draw(); -} - void LLInventoryView::setFilterTextFromFilter() { mFilterText = mActivePanel->getFilter()->getFilterText(); @@ -1040,9 +903,23 @@ void LLInventoryView::toggleFindOptions() mFloaterControls[std::string("Inventory.ShowFilters")]->setValue(FALSE); } } + +void LLInventoryView::setSelectCallback(const LLFolderView::signal_t::slot_type& cb) +{ + getChild("All Items")->setSelectCallback(cb); + getChild("Recent Items")->setSelectCallback(cb); + getChild("Worn Items")->setSelectCallback(cb); +} + +void LLInventoryView::onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action) +{ + panel->onSelectionChange(items, user_action); +} + ///---------------------------------------------------------------------------- /// LLFloaterInventoryFinder ///---------------------------------------------------------------------------- + LLFloaterInventoryFinder* LLInventoryView::getFinder() { return (LLFloaterInventoryFinder*)mFinderHandle.get(); @@ -1056,7 +933,7 @@ LLFloaterInventoryFinder::LLFloaterInventoryFinder(const std::string& name, INV_FINDER_WIDTH, INV_FINDER_HEIGHT, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES), mInventoryView(inventory_view), - mFilter(inventory_view->mActivePanel->getFilter()) + mFilter(inventory_view->getPanel()->getFilter()) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml"); @@ -1065,6 +942,9 @@ LLFloaterInventoryFinder::LLFloaterInventoryFinder(const std::string& name, BOOL LLFloaterInventoryFinder::postBuild() { + const LLRect& viewrect = mInventoryView->getRect(); + setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight())); + childSetAction("All", selectAllTypes, this); childSetAction("None", selectNoTypes, this); @@ -1074,30 +954,12 @@ BOOL LLFloaterInventoryFinder::postBuild() mSpinSinceDays = getChild("spin_days_ago"); childSetCommitCallback("spin_days_ago", onTimeAgo, this); -// mCheckSinceLogoff = getChild("check_since_logoff"); - childSetCommitCallback("check_since_logoff", onCheckSinceLogoff, this); - childSetAction("Close", onCloseBtn, this); updateElementsFromFilter(); return TRUE; } - -void LLFloaterInventoryFinder::onCheckSinceLogoff(LLUICtrl *ctrl, void *user_data) -{ - LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; - if (!self) return; - - bool since_logoff= self->childGetValue("check_since_logoff"); - - if (!since_logoff && - !( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) ) - { - self->mSpinSinceHours->set(1.0f); - } -} - void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data) { LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; @@ -1123,7 +985,7 @@ void LLFloaterInventoryFinder::updateElementsFromFilter() return; // Get data needed for filter display - U32 filter_types = mFilter->getFilterTypes(); + U32 filter_types = mFilter->getFilterObjectTypes(); std::string filter_string = mFilter->getFilterSubString(); LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState(); U32 hours = mFilter->getHoursAgo(); @@ -1231,9 +1093,9 @@ void LLFloaterInventoryFinder::draw() } // update the panel, panel will update the filter - mInventoryView->mActivePanel->setShowFolderState(getCheckShowEmpty() ? + mInventoryView->getPanel()->setShowFolderState(getCheckShowEmpty() ? LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mInventoryView->mActivePanel->setFilterTypes(filter); + mInventoryView->getPanel()->setFilterTypes(filter); if (getCheckSinceLogoff()) { mSpinSinceDays->set(0); @@ -1249,8 +1111,8 @@ void LLFloaterInventoryFinder::draw() mSpinSinceHours->set((F32)hours); } hours += days * 24; - mInventoryView->mActivePanel->setHoursAgo(hours); - mInventoryView->mActivePanel->setSinceLogoff(getCheckSinceLogoff()); + mInventoryView->getPanel()->setHoursAgo(hours); + mInventoryView->getPanel()->setSinceLogoff(getCheckSinceLogoff()); mInventoryView->setFilterTextFromFilter(); LLFloater::draw(); @@ -1304,18 +1166,6 @@ void LLFloaterInventoryFinder::selectAllTypes(void* user_data) self->childSetValue("check_sound", TRUE); self->childSetValue("check_texture", TRUE); self->childSetValue("check_snapshot", TRUE); - -/* - self->mCheckCallingCard->set(TRUE); - self->mCheckClothing->set(TRUE); - self->mCheckGesture->set(TRUE); - self->mCheckLandmark->set(TRUE); - self->mCheckNotecard->set(TRUE); - self->mCheckObject->set(TRUE); - self->mCheckScript->set(TRUE); - self->mCheckSound->set(TRUE); - self->mCheckTexture->set(TRUE); - self->mCheckSnapshot->set(TRUE);*/ } //static @@ -1324,20 +1174,6 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data) LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data; if(!self) return; - /* - self->childSetValue("check_animation", FALSE); - self->mCheckCallingCard->set(FALSE); - self->mCheckClothing->set(FALSE); - self->mCheckGesture->set(FALSE); - self->mCheckLandmark->set(FALSE); - self->mCheckNotecard->set(FALSE); - self->mCheckObject->set(FALSE); - self->mCheckScript->set(FALSE); - self->mCheckSound->set(FALSE); - self->mCheckTexture->set(FALSE); - self->mCheckSnapshot->set(FALSE);*/ - - self->childSetValue("check_animation", FALSE); self->childSetValue("check_calling_card", FALSE); self->childSetValue("check_clothing", FALSE); diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 43d4184d1..44f6c7f09 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -89,6 +89,8 @@ public: const LLInventoryPanel* getActivePanel() const { return mActivePanel; } const std::string& getFilterText() const { return mFilterText; } + + void setSelectCallback(const LLFolderView::signal_t::slot_type& cb); static void onSearchEdit(const std::string& search_string, void* user_data ); // // Misc functions @@ -97,7 +99,8 @@ public: void startSearch(); void toggleFindOptions(); - static void onSelectionChange(const std::deque &items, BOOL user_action, void* data); + void onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action); + static BOOL filtersVisible(void* user_data); static void onClearSearch(void* user_data); static void onFoldersByName(void *user_data); @@ -121,7 +124,7 @@ public: void updateSortControls(); void resetFilters(); - + void updateItemcountText(); // [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) static void closeAll() @@ -163,9 +166,10 @@ protected: LLTabContainer* mFilterTabs; LLHandle mFinderHandle; LLInventoryPanel* mActivePanel; + bool mResortActivePanel; LLSaveFolderState* mSavedFolderState; - std::string mFilterText; + //std::string mFilterSubString; // This container is used to hold all active inventory views. This diff --git a/indra/newview/llpanelmediahud.cpp b/indra/newview/llpanelmediahud.cpp index 2d0aa931c..2fcee26c6 100644 --- a/indra/newview/llpanelmediahud.cpp +++ b/indra/newview/llpanelmediahud.cpp @@ -129,20 +129,20 @@ BOOL LLPanelMediaHUD::postBuild() LLButton* scroll_up_btn = getChild("scrollup"); scroll_up_btn->setClickedCallback(onScrollUp, this); - scroll_up_btn->setHeldDownCallback(onScrollUpHeld); - scroll_up_btn->setMouseUpCallback(onScrollStop); + scroll_up_btn->setHeldDownCallback(onScrollUpHeld, this); + scroll_up_btn->setMouseUpCallback(onScrollStop, this); LLButton* scroll_left_btn = getChild("scrollleft"); scroll_left_btn->setClickedCallback(onScrollLeft, this); - scroll_left_btn->setHeldDownCallback(onScrollLeftHeld); - scroll_left_btn->setMouseUpCallback(onScrollStop); + scroll_left_btn->setHeldDownCallback(onScrollLeftHeld, this); + scroll_left_btn->setMouseUpCallback(onScrollStop, this); LLButton* scroll_right_btn = getChild("scrollright"); scroll_right_btn->setClickedCallback(onScrollRight, this); - scroll_right_btn->setHeldDownCallback(onScrollLeftHeld); - scroll_right_btn->setMouseUpCallback(onScrollStop); + scroll_right_btn->setHeldDownCallback(onScrollLeftHeld, this); + scroll_right_btn->setMouseUpCallback(onScrollStop, this); LLButton* scroll_down_btn = getChild("scrolldown"); scroll_down_btn->setClickedCallback(onScrollDown, this); - scroll_down_btn->setHeldDownCallback(onScrollDownHeld); - scroll_down_btn->setMouseUpCallback(onScrollStop); + scroll_down_btn->setHeldDownCallback(onScrollDownHeld, this); + scroll_down_btn->setMouseUpCallback(onScrollStop, this); mMouseInactiveTime = gSavedSettings.getF32("MediaControlTimeout"); mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 935d0af1b..bee2b1bd2 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -55,7 +55,7 @@ #include "llinventoryfunctions.h" #include "llmanipscale.h" #include "llnotificationsutil.h" -#include "llpanelinventory.h" +#include "llpanelobjectinventory.h" #include "llpreviewscript.h" #include "llresmgr.h" #include "llselectmgr.h" diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h index 05d339d3a..504e3d94f 100644 --- a/indra/newview/llpanelobject.h +++ b/indra/newview/llpanelobject.h @@ -45,7 +45,7 @@ class LLUICtrl; class LLButton; class LLViewerObject; class LLComboBox; -class LLPanelInventory; +class LLPanelObjectInventory; class LLColorSwatchCtrl; class LLTextureCtrl; class LLInventoryItem; diff --git a/indra/newview/llpanelinventory.cpp b/indra/newview/llpanelobjectinventory.cpp similarity index 78% rename from indra/newview/llpanelinventory.cpp rename to indra/newview/llpanelobjectinventory.cpp index 3c318a700..83889fb4a 100644 --- a/indra/newview/llpanelinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1,36 +1,30 @@ /** - * @file llpanelinventory.cpp - * @brief LLPanelInventory class implementation + * @file llsidepanelinventory.cpp + * @brief LLPanelObjectInventory class implementation * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -// ***************************************************************************** +//***************************************************************************** // // Implementation of the panel inventory - used to view and control a // task's inventory. @@ -39,66 +33,43 @@ #include "llviewerprecompiledheaders.h" -#include // for std::ostringstream -#include // for std::pair<> +#include "llpanelobjectinventory.h" -#include "stdenums.h" -#include "llpanelinventory.h" - -#include "message.h" -#include "lldarray.h" -#include "llfontgl.h" -#include "llassetstorage.h" -#include "llfoldervieweventlistener.h" -#include "llinventory.h" -#include "llinventorybridge.h" -#include "llinventorydefines.h" -#include "llinventoryicon.h" +#include "llmenugl.h" #include "llnotificationsutil.h" +#include "roles_constants.h" #include "llagent.h" #include "llcallbacklist.h" -#include "llfocusmgr.h" +#include "llfloaterbuycontents.h" #include "llfloaterbuycurrency.h" -#include "llfloaterproperties.h" #include "llfolderview.h" -#include "llgl.h" -#include "llinventorymodel.h" -#include "llinventoryicon.h" #include "llinventorybridge.h" +#include "llinventorydefines.h" #include "llinventoryfilter.h" -#include "llmenugl.h" +#include "llinventoryfunctions.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" #include "llpreviewscript.h" #include "llpreviewsound.h" #include "llpreviewtexture.h" -#include "roles_constants.h" #include "llscrollcontainer.h" #include "llselectmgr.h" #include "llstatusbar.h" #include "lltooldraganddrop.h" -#include "llviewercontrol.h" -#include "llviewerregion.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewermessage.h" -#include "llviewerobject.h" -#include "llviewerobjectlist.h" -#include "llviewerwindow.h" -#include "llwearable.h" +#include "lltrans.h" #include "llviewerassettype.h" +#include "llviewerinventory.h" +#include "llviewerregion.h" +#include "llviewerobjectlist.h" +#include "llviewermessage.h" // [RLVa:KB] - Checked: 2010-03-27 (RLVa-1.2.0b) #include "rlvhandler.h" // [/RLVa:KB] #include "hippogridmanager.h" -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - ///---------------------------------------------------------------------------- /// Class LLTaskInvFVBridge @@ -110,23 +81,25 @@ protected: LLUUID mUUID; std::string mName; mutable std::string mDisplayName; - LLPanelInventory* mPanel; + LLPanelObjectInventory* mPanel; U32 mFlags; + LLAssetType::EType mAssetType; + LLInventoryType::EType mInventoryType; + LLInventoryObject* findInvObject() const; LLInventoryItem* findItem() const; public: - LLTaskInvFVBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name, + LLTaskInvFVBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name, U32 flags=0); - virtual ~LLTaskInvFVBridge( void ) {} + virtual ~LLTaskInvFVBridge( ) {} virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } - static LLTaskInvFVBridge* createObjectBridge(LLPanelInventory* panel, + static LLTaskInvFVBridge* createObjectBridge(LLPanelObjectInventory* panel, LLInventoryObject* object); void showProperties(); void buyItem(); @@ -137,30 +110,35 @@ public: virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; virtual PermissionMask getPermissionMask() const { return PERM_NONE; } + /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } virtual time_t getCreationDate() const; virtual LLUIImagePtr getIcon() const; virtual void openItem(); + virtual BOOL canOpenItem() const { return FALSE; } + virtual void closeItem() {} virtual void previewItem(); virtual void selectItem() {} virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); - virtual BOOL isItemMovable(); - virtual BOOL isItemRemovable(); + virtual BOOL isItemMovable() const; + virtual BOOL isItemRemovable() const; virtual BOOL removeItem(); virtual void removeBatch(LLDynamicArray& batch); virtual void move(LLFolderViewEventListener* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; - virtual BOOL cutToClipboard() const; + virtual void cutToClipboard(); virtual BOOL isClipboardPasteable() const; virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual BOOL isUpToDate() const { return TRUE; } virtual BOOL hasChildren() const { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return LLInventoryType::IT_NONE; } + virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + // LLDragAndDropBridge functionality virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, @@ -171,26 +149,39 @@ public: }; LLTaskInvFVBridge::LLTaskInvFVBridge( - LLPanelInventory* panel, + LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags): mUUID(uuid), mName(name), mPanel(panel), - mFlags(flags) + mFlags(flags), + mAssetType(LLAssetType::AT_NONE), + mInventoryType(LLInventoryType::IT_NONE) { - + const LLInventoryItem *item = findItem(); + if (item) + { + mAssetType = item->getType(); + mInventoryType = item->getInventoryType(); + } } +LLInventoryObject* LLTaskInvFVBridge::findInvObject() const +{ + LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); + if (object) + { + return object->getInventoryObject(mUUID); + } + return NULL; +} + + LLInventoryItem* LLTaskInvFVBridge::findItem() const { - LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); - if(object) - { - return (LLInventoryItem*)(object->getInventoryObject(mUUID)); - } - return NULL; + return dynamic_cast(findInvObject()); } void LLTaskInvFVBridge::showProperties() @@ -275,7 +266,7 @@ void LLTaskInvFVBridge::buyItem() payload["task_id"] = inv->mTaskID; payload["item_id"] = inv->mItemID; payload["type"] = inv->mType; - LLNotifications::instance().add(alertdesc, args, payload, LLTaskInvFVBridge::commitBuyItem); + LLNotificationsUtil::add(alertdesc, args, payload, LLTaskInvFVBridge::commitBuyItem); } } @@ -295,7 +286,7 @@ S32 LLTaskInvFVBridge::getPrice() // static bool LLTaskInvFVBridge::commitBuyItem(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) { LLViewerObject* object = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); @@ -311,6 +302,7 @@ bool LLTaskInvFVBridge::commitBuyItem(const LLSD& notification, const LLSD& resp msg->addUUIDFast(_PREHASH_ObjectID, notification["payload"]["task_id"].asUUID()); msg->addUUIDFast(_PREHASH_ItemID, notification["payload"]["item_id"].asUUID()); msg->addUUIDFast(_PREHASH_FolderID, + //"type" should be LLAssetType::AssetType, not LLFolderType::EType gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType((LLAssetType::EType)notification["payload"]["type"].asInteger()))); msg->sendReliable(object->getRegion()->getHost()); } @@ -336,15 +328,15 @@ const std::string& LLTaskInvFVBridge::getDisplayName() const if(!copy) { - mDisplayName.append(" (no copy)"); + mDisplayName.append(LLTrans::getString("no_copy")); } if(!mod) { - mDisplayName.append(" (no modify)"); + mDisplayName.append(LLTrans::getString("no_modify")); } if(!xfer) { - mDisplayName.append(" (no transfer)"); + mDisplayName.append(LLTrans::getString("no_transfer")); } } @@ -359,13 +351,9 @@ time_t LLTaskInvFVBridge::getCreationDate() const LLUIImagePtr LLTaskInvFVBridge::getIcon() const { - BOOL item_is_multi = FALSE; - if ( mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) - { - item_is_multi = TRUE; - } + const BOOL item_is_multi = (mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS); - return LLInventoryIcon::getIcon(LLAssetType::AT_OBJECT, LLInventoryType::IT_OBJECT, 0, item_is_multi ); + return LLInventoryIcon::getIcon(mAssetType, mInventoryType, 0, item_is_multi ); } void LLTaskInvFVBridge::openItem() @@ -393,8 +381,7 @@ BOOL LLTaskInvFVBridge::isItemRenameable() const // LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(object) { - LLInventoryItem* item; - item = (LLInventoryItem*)(object->getInventoryObject(mUUID)); + LLInventoryItem* item = (LLInventoryItem*)(object->getInventoryObject(mUUID)); if(item && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE, GOD_LIKE)) { @@ -433,7 +420,7 @@ BOOL LLTaskInvFVBridge::renameItem(const std::string& new_name) return TRUE; } -BOOL LLTaskInvFVBridge::isItemMovable() +BOOL LLTaskInvFVBridge::isItemMovable() const { //LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); //if(object && (object->permModify() || gAgent.isGodlike())) @@ -463,7 +450,7 @@ BOOL LLTaskInvFVBridge::isItemMovable() return TRUE; } -BOOL LLTaskInvFVBridge::isItemRemovable() +BOOL LLTaskInvFVBridge::isItemRemovable() const { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); // [RLVa:KB] - Checked: 2010-04-01 (RLVa-1.2.0c) | Modified: RLVa-1.0.5a @@ -491,13 +478,9 @@ BOOL LLTaskInvFVBridge::isItemRemovable() return FALSE; } -// helper for remove -typedef std::pair > two_uuids_list_t; -typedef std::pair remove_data_t; - -bool remove_task_inventory_callback(const LLSD& notification, const LLSD& response, LLPanelInventory* panel) +bool remove_task_inventory_callback(const LLSD& notification, const LLSD& response, LLPanelObjectInventory* panel) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLViewerObject* object = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); if(option == 0 && object) { @@ -516,6 +499,10 @@ bool remove_task_inventory_callback(const LLSD& notification, const LLSD& respon return false; } +// helper for remove +// ! REFACTOR ! two_uuids_list_t is also defined in llinventorybridge.h, but differently. +typedef std::pair > panel_two_uuids_list_t; +typedef std::pair remove_data_t; BOOL LLTaskInvFVBridge::removeItem() { if(isItemRemovable() && mPanel) @@ -531,10 +518,6 @@ BOOL LLTaskInvFVBridge::removeItem() } else { - remove_data_t* data = new remove_data_t; - data->first = mPanel; - data->second.first = mPanel->getTaskUUID(); - data->second.second.push_back(mUUID); LLSD payload; payload["task_id"] = mPanel->getTaskUUID(); payload["inventory_ids"].append(mUUID); @@ -603,9 +586,8 @@ BOOL LLTaskInvFVBridge::copyToClipboard() const return FALSE; } -BOOL LLTaskInvFVBridge::cutToClipboard() const +void LLTaskInvFVBridge::cutToClipboard() { - return FALSE; } BOOL LLTaskInvFVBridge::isClipboardPasteable() const @@ -680,7 +662,7 @@ BOOL LLTaskInvFVBridge::dragOrDrop(MASK mask, BOOL drop, //} // virtual -void LLTaskInvFVBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLTaskInvFVBridge::performAction(LLInventoryModel* model, std::string action) { if (action == "task_buy") { @@ -757,7 +739,7 @@ void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } } } - else + else if (canOpenItem()) { items.push_back(std::string("Task Open")); if (!isItemCopyable()) @@ -812,25 +794,28 @@ class LLTaskCategoryBridge : public LLTaskInvFVBridge { public: LLTaskCategoryBridge( - LLPanelInventory* panel, + LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name); virtual LLUIImagePtr getIcon() const; - virtual const std::string& getDisplayName() const { return getName(); } + virtual const std::string& getDisplayName() const; virtual BOOL isItemRenameable() const; + // virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL renameItem(const std::string& new_name); - virtual BOOL isItemRemovable(); + virtual BOOL isItemRemovable() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL hasChildren() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data); + virtual BOOL canOpenItem() const { return TRUE; } + virtual void openItem(); }; LLTaskCategoryBridge::LLTaskCategoryBridge( - LLPanelInventory* panel, + LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) @@ -842,6 +827,12 @@ LLUIImagePtr LLTaskCategoryBridge::getIcon() const return LLUI::getUIImage("inv_folder_plain_closed.tga"); } +// virtual +const std::string& LLTaskCategoryBridge::getDisplayName() const +{ + return getName(); +} + BOOL LLTaskCategoryBridge::isItemRenameable() const { return FALSE; @@ -852,7 +843,7 @@ BOOL LLTaskCategoryBridge::renameItem(const std::string& new_name) return FALSE; } -BOOL LLTaskCategoryBridge::isItemRemovable() +BOOL LLTaskCategoryBridge::isItemRemovable() const { return FALSE; } @@ -861,7 +852,6 @@ void LLTaskCategoryBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { std::vector items; std::vector disabled_items; - items.push_back(std::string("Task Open")); // *TODO: Translate hide_context_entries(menu, items, disabled_items); } @@ -872,10 +862,14 @@ BOOL LLTaskCategoryBridge::hasChildren() const return FALSE; } +void LLTaskCategoryBridge::openItem() +{ +} + BOOL LLTaskCategoryBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { //llinfos << "LLTaskInvFVBridge::startDrag()" << llendl; - if(mPanel) + if(mPanel && mUUID.notNull()) { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(object) @@ -915,24 +909,19 @@ BOOL LLTaskCategoryBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: - // case DAD_CALLINGCARD: - // - // *HACK: In order to resolve SL-22177, we need to block - // drags from notecards and objects onto other - // objects. uncomment the simpler version when we have - // that right. + case DAD_MESH: accept = LLToolDragAndDrop::isInventoryDropAcceptable(object, (LLViewerInventoryItem*)cargo_data); // testzone //if(LLToolDragAndDrop::isInventoryDropAcceptable( // object, (LLViewerInventoryItem*)cargo_data) - if(object->permModify() + /*if(object->permModify() // && (LLToolDragAndDrop::SOURCE_WORLD != LLToolDragAndDrop::getInstance()->getSource()) && (LLToolDragAndDrop::SOURCE_NOTECARD != LLToolDragAndDrop::getInstance()->getSource())) { accept = TRUE; - } + }*/ if(accept && drop) { LLToolDragAndDrop::dropInventory(object, @@ -965,9 +954,6 @@ BOOL LLTaskCategoryBridge::dragOrDrop(MASK mask, BOOL drop, LLToolDragAndDrop::getInstance()->getSourceID()); } break; - // - //case DAD_CALLINGCARD: - // default: break; } @@ -982,33 +968,15 @@ BOOL LLTaskCategoryBridge::dragOrDrop(MASK mask, BOOL drop, class LLTaskTextureBridge : public LLTaskInvFVBridge { public: - LLTaskTextureBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name, - LLInventoryType::EType it); + LLTaskTextureBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); -protected: - LLInventoryType::EType mInventoryType; }; -LLTaskTextureBridge::LLTaskTextureBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name, - LLInventoryType::EType it) : - LLTaskInvFVBridge(panel, uuid, name), - mInventoryType(it) -{ -} - -LLUIImagePtr LLTaskTextureBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_TEXTURE, mInventoryType, 0, FALSE); -} - void LLTaskTextureBridge::openItem() { // [RLVa:KB] - Checked: 2009-11-11 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a @@ -1044,31 +1012,18 @@ void LLTaskTextureBridge::openItem() class LLTaskSoundBridge : public LLTaskInvFVBridge { public: - LLTaskSoundBridge( - LLPanelInventory* panel, + LLTaskSoundBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, - const std::string& name); + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); - virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void* data); }; -LLTaskSoundBridge::LLTaskSoundBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskSoundBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0, FALSE); -} - void LLTaskSoundBridge::openItem() { openSoundPreview((void*)this); @@ -1077,7 +1032,8 @@ void LLTaskSoundBridge::openItem() void LLTaskSoundBridge::openSoundPreview(void* data) { LLTaskSoundBridge* self = (LLTaskSoundBridge*)data; - if(!self) return; + if(!self) + return; if(!LLPreview::show(self->mUUID)) { // There isn't one, so make a new preview @@ -1095,7 +1051,7 @@ void LLTaskSoundBridge::openSoundPreview(void* data) } // virtual -void LLTaskSoundBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +void LLTaskSoundBridge::performAction(LLInventoryModel* model, std::string action) { if (action == "task_play") { @@ -1105,7 +1061,7 @@ void LLTaskSoundBridge::performAction(LLFolderView* folder, LLInventoryModel* mo send_sound_trigger(item->getAssetUUID(), 1.0); } } - LLTaskInvFVBridge::performAction(folder, model, action); + LLTaskInvFVBridge::performAction(model, action); } void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) @@ -1147,9 +1103,9 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } } } - else + else if (canOpenItem()) { - items.push_back(std::string("Task Open")); + //items.push_back(std::string("Task Open")); if (!isItemCopyable()) { disabled_items.push_back(std::string("Task Open")); @@ -1166,7 +1122,7 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } items.push_back(std::string("Task Play")); - /*menu.appendSeparator(); + /*menu.addSeparator(); menu.append(new LLMenuItemCallGL("Play", &LLTaskSoundBridge::playSound, NULL, @@ -1182,28 +1138,12 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) class LLTaskLandmarkBridge : public LLTaskInvFVBridge { public: - LLTaskLandmarkBridge( - LLPanelInventory* panel, + LLTaskLandmarkBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, - const std::string& name); - - virtual LLUIImagePtr getIcon() const; + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} }; -LLTaskLandmarkBridge::LLTaskLandmarkBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskLandmarkBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, 0, FALSE); -} - - ///---------------------------------------------------------------------------- /// Class LLTaskCallingCardBridge ///---------------------------------------------------------------------------- @@ -1211,29 +1151,15 @@ LLUIImagePtr LLTaskLandmarkBridge::getIcon() const class LLTaskCallingCardBridge : public LLTaskInvFVBridge { public: - LLTaskCallingCardBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskCallingCardBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); }; -LLTaskCallingCardBridge::LLTaskCallingCardBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskCallingCardBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, 0, FALSE); -} - BOOL LLTaskCallingCardBridge::isItemRenameable() const { return FALSE; @@ -1252,37 +1178,23 @@ BOOL LLTaskCallingCardBridge::renameItem(const std::string& new_name) class LLTaskScriptBridge : public LLTaskInvFVBridge { public: - LLTaskScriptBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskScriptBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; //static BOOL enableIfCopyable( void* userdata ); }; -LLTaskScriptBridge::LLTaskScriptBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskScriptBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE); -} - - class LLTaskLSLBridge : public LLTaskScriptBridge { public: - LLTaskLSLBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskLSLBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskScriptBridge(panel, uuid, name) {} + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); virtual BOOL removeItem(); //virtual void buildContextMenu(LLMenuGL& menu); @@ -1290,14 +1202,6 @@ public: //static void copyToInventory(void* userdata); }; -LLTaskLSLBridge::LLTaskLSLBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskScriptBridge(panel, uuid, name) -{ -} - void LLTaskLSLBridge::openItem() { llinfos << "LLTaskLSLBridge::openItem() " << mUUID << llendl; @@ -1361,33 +1265,13 @@ BOOL LLTaskLSLBridge::removeItem() class LLTaskObjectBridge : public LLTaskInvFVBridge { public: - LLTaskObjectBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); - - virtual LLUIImagePtr getIcon() const; + LLTaskObjectBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name, + U32 flags = 0) : + LLTaskInvFVBridge(panel, uuid, name, flags) {} }; -LLTaskObjectBridge::LLTaskObjectBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskObjectBridge::getIcon() const -{ - BOOL item_is_multi = FALSE; - if ( mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) - { - item_is_multi = TRUE; - } - - return LLInventoryIcon::getIcon(LLAssetType::AT_OBJECT, LLInventoryType::IT_OBJECT, 0, item_is_multi); -} - ///---------------------------------------------------------------------------- /// Class LLTaskNotecardBridge ///---------------------------------------------------------------------------- @@ -1395,29 +1279,16 @@ LLUIImagePtr LLTaskObjectBridge::getIcon() const class LLTaskNotecardBridge : public LLTaskInvFVBridge { public: - LLTaskNotecardBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskNotecardBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); virtual BOOL removeItem(); }; -LLTaskNotecardBridge::LLTaskNotecardBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskNotecardBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0, FALSE); -} - void LLTaskNotecardBridge::openItem() { if(LLPreview::show(mUUID)) @@ -1469,29 +1340,16 @@ BOOL LLTaskNotecardBridge::removeItem() class LLTaskGestureBridge : public LLTaskInvFVBridge { public: - LLTaskGestureBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskGestureBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); virtual BOOL removeItem(); }; -LLTaskGestureBridge::LLTaskGestureBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskGestureBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0, FALSE); -} - void LLTaskGestureBridge::openItem() { if(LLPreview::show(mUUID)) @@ -1529,29 +1387,16 @@ BOOL LLTaskGestureBridge::removeItem() class LLTaskAnimationBridge : public LLTaskInvFVBridge { public: - LLTaskAnimationBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name); + LLTaskAnimationBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name) : + LLTaskInvFVBridge(panel, uuid, name) {} - virtual LLUIImagePtr getIcon() const; + virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); virtual BOOL removeItem(); }; -LLTaskAnimationBridge::LLTaskAnimationBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name) : - LLTaskInvFVBridge(panel, uuid, name) -{ -} - -LLUIImagePtr LLTaskAnimationBridge::getIcon() const -{ - return LLInventoryIcon::getIcon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0, FALSE); -} - void LLTaskAnimationBridge::openItem() { if(LLPreview::show(mUUID)) @@ -1584,7 +1429,7 @@ void LLTaskAnimationBridge::openItem() rect, getName(), mUUID, - 0, + LLPreviewAnim::NONE, mPanel->getTaskUUID()); preview->setFocus(TRUE); // take focus if you're looking at one of these @@ -1607,115 +1452,101 @@ BOOL LLTaskAnimationBridge::removeItem() class LLTaskWearableBridge : public LLTaskInvFVBridge { public: - LLTaskWearableBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name, - LLAssetType::EType asset_type, - U32 flags); + LLTaskWearableBridge(LLPanelObjectInventory* panel, + const LLUUID& uuid, + const std::string& name, + U32 flags) : + LLTaskInvFVBridge(panel, uuid, name, flags) {} virtual LLUIImagePtr getIcon() const; - -protected: - LLAssetType::EType mAssetType; }; -LLTaskWearableBridge::LLTaskWearableBridge( - LLPanelInventory* panel, - const LLUUID& uuid, - const std::string& name, - LLAssetType::EType asset_type, - U32 flags) : - LLTaskInvFVBridge(panel, uuid, name, flags), - mAssetType( asset_type ) -{ -} - LLUIImagePtr LLTaskWearableBridge::getIcon() const { - return LLInventoryIcon::getIcon(mAssetType, LLInventoryType::IT_WEARABLE, mFlags, FALSE ); + return LLInventoryIcon::getIcon(mAssetType, mInventoryType, mFlags, FALSE ); } - ///---------------------------------------------------------------------------- /// LLTaskInvFVBridge impl //---------------------------------------------------------------------------- -LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelInventory* panel, +LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory* panel, LLInventoryObject* object) { LLTaskInvFVBridge* new_bridge = NULL; - LLAssetType::EType type = object->getType(); - LLInventoryItem* item = NULL; + const LLInventoryItem* item = dynamic_cast(object); + const U32 itemflags = ( NULL == item ? 0 : item->getFlags() ); + LLAssetType::EType type = object ? object->getType() : LLAssetType::AT_CATEGORY; + LLUUID object_id = object ? object->getUUID() : LLUUID::null; + std::string object_name = object ? object->getName() : std::string(); + switch(type) { case LLAssetType::AT_TEXTURE: - item = (LLInventoryItem*)object; new_bridge = new LLTaskTextureBridge(panel, - object->getUUID(), - object->getName(), - item->getInventoryType()); + object_id, + object_name); break; case LLAssetType::AT_SOUND: new_bridge = new LLTaskSoundBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_LANDMARK: new_bridge = new LLTaskLandmarkBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_CALLINGCARD: new_bridge = new LLTaskCallingCardBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_SCRIPT: // OLD SCRIPTS DEPRECATED - JC llwarns << "Old script" << llendl; //new_bridge = new LLTaskOldScriptBridge(panel, - // object->getUUID(), - // object->getName()); + // object_id, + // object_name); break; case LLAssetType::AT_OBJECT: new_bridge = new LLTaskObjectBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name, + itemflags); break; case LLAssetType::AT_NOTECARD: new_bridge = new LLTaskNotecardBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_ANIMATION: new_bridge = new LLTaskAnimationBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_GESTURE: new_bridge = new LLTaskGestureBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_CLOTHING: case LLAssetType::AT_BODYPART: - item = (LLInventoryItem*)object; new_bridge = new LLTaskWearableBridge(panel, - object->getUUID(), - object->getName(), - type, - item->getFlags()); + object_id, + object_name, + itemflags); break; case LLAssetType::AT_CATEGORY: new_bridge = new LLTaskCategoryBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); break; case LLAssetType::AT_LSL_TEXT: new_bridge = new LLTaskLSLBridge(panel, - object->getUUID(), - object->getName()); + object_id, + object_name); + break; break; default: llinfos << "Unhandled inventory type (llassetstorage.h): " @@ -1727,11 +1558,11 @@ LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelInventory* panel ///---------------------------------------------------------------------------- -/// Class LLPanelInventory +/// Class LLPanelObjectInventory ///---------------------------------------------------------------------------- // Default constructor -LLPanelInventory::LLPanelInventory(const std::string& name, const LLRect& rect) : +LLPanelObjectInventory::LLPanelObjectInventory(const std::string& name, const LLRect& rect) : LLPanel(name, rect), mScroller(NULL), mFolders(NULL), @@ -1746,16 +1577,16 @@ LLPanelInventory::LLPanelInventory(const std::string& name, const LLRect& rect) } // Destroys the object -LLPanelInventory::~LLPanelInventory() +LLPanelObjectInventory::~LLPanelObjectInventory() { if (!gIdleCallbacks.deleteFunction(idle, this)) { - llwarns << "LLPanelInventory::~LLPanelInventory() failed to delete callback" << llendl; + llwarns << "LLPanelObjectInventory::~LLPanelObjectInventory() failed to delete callback" << llendl; } } -void LLPanelInventory::clearContents() +void LLPanelObjectInventory::clearContents() { mHaveInventory = FALSE; mIsInventoryEmpty = TRUE; @@ -1775,14 +1606,14 @@ void LLPanelInventory::clearContents() } -void LLPanelInventory::reset() +void LLPanelObjectInventory::reset() { clearContents(); setBorderVisible(FALSE); LLRect dummy_rect(0, 1, 1, 0); - mFolders = new LLFolderView(std::string("task inventory"), NULL, dummy_rect, getTaskUUID(), this); + mFolders = new LLFolderView(std::string("task inventory"), dummy_rect, getTaskUUID(), this, LLTaskInvFVBridge::createObjectBridge(this, NULL)); // this ensures that we never say "searching..." or "no items found" mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); @@ -1794,7 +1625,7 @@ void LLPanelInventory::reset() mFolders->setScrollContainer( mScroller ); } -void LLPanelInventory::inventoryChanged(LLViewerObject* object, +void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, LLInventoryObject::object_list_t* inventory, S32 serial_num, void* data) @@ -1837,7 +1668,7 @@ void LLPanelInventory::inventoryChanged(LLViewerObject* object, } } -void LLPanelInventory::updateInventory() +void LLPanelObjectInventory::updateInventory() { //llinfos << "inventory arrived: \n" // << " panel UUID: " << panel->mTaskUUID << "\n" @@ -1845,9 +1676,9 @@ void LLPanelInventory::updateInventory() // We're still interested in this task's inventory. std::set selected_items; BOOL inventory_has_focus = FALSE; - if (mHaveInventory && mFolders->getNumSelectedDescendants()) + if (mHaveInventory) { - mFolders->getSelectionList(selected_items); + selected_items = mFolders->getSelectionList(); inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); } @@ -1901,7 +1732,7 @@ void LLPanelInventory::updateInventory() } } - mFolders->arrangeFromRoot(); + mFolders->requestArrange(); mInventoryNeedsUpdate = FALSE; } @@ -1910,7 +1741,7 @@ void LLPanelInventory::updateInventory() // leads to an N^2 based on the category count. This could be greatly // speeded with an efficient multimap implementation, but we don't // have that in our current arsenal. -void LLPanelInventory::createFolderViews(LLInventoryObject* inventory_root, LLInventoryObject::object_list_t& contents) +void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root, LLInventoryObject::object_list_t& contents) { if (!inventory_root) { @@ -1924,6 +1755,8 @@ void LLPanelInventory::createFolderViews(LLInventoryObject* inventory_root, LLIn LLFolderViewFolder* new_folder = NULL; new_folder = new LLFolderViewFolder(inventory_root->getName(), bridge->getIcon(), + bridge->getOpenIcon(), + NULL, mFolders, bridge); new_folder->addToFolder(mFolders, mFolders); @@ -1935,7 +1768,7 @@ void LLPanelInventory::createFolderViews(LLInventoryObject* inventory_root, LLIn typedef std::pair obj_folder_pair; -void LLPanelInventory::createViewsForCategory(LLInventoryObject::object_list_t* inventory, +void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_list_t* inventory, LLInventoryObject* parent, LLFolderViewFolder* folder) { @@ -1961,6 +1794,8 @@ void LLPanelInventory::createViewsForCategory(LLInventoryObject::object_list_t* { view = new LLFolderViewFolder(obj->getName(), bridge->getIcon(), + bridge->getOpenIcon(), + NULL, mFolders, bridge); child_categories.put(new obj_folder_pair(obj, @@ -1970,6 +1805,8 @@ void LLPanelInventory::createViewsForCategory(LLInventoryObject::object_list_t* { view = new LLFolderViewItem(obj->getName(), bridge->getIcon(), + bridge->getOpenIcon(), + NULL, bridge->getCreationDate(), mFolders, bridge); @@ -1987,9 +1824,9 @@ void LLPanelInventory::createViewsForCategory(LLInventoryObject::object_list_t* } } -void LLPanelInventory::refresh() +void LLPanelObjectInventory::refresh() { - //llinfos << "LLPanelInventory::refresh()" << llendl; + //llinfos << "LLPanelObjectInventory::refresh()" << llendl; BOOL has_inventory = FALSE; const BOOL non_root_ok = TRUE; LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(NULL, non_root_ok); @@ -2042,10 +1879,10 @@ void LLPanelInventory::refresh() removeVOInventoryListener(); clearContents(); } - //llinfos << "LLPanelInventory::refresh() " << mTaskUUID << llendl; + //llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl; } -void LLPanelInventory::removeSelectedItem() +void LLPanelObjectInventory::removeSelectedItem() { if(mFolders) { @@ -2053,7 +1890,7 @@ void LLPanelInventory::removeSelectedItem() } } -void LLPanelInventory::startRenamingSelectedItem() +void LLPanelObjectInventory::startRenamingSelectedItem() { if(mFolders) { @@ -2061,7 +1898,7 @@ void LLPanelInventory::startRenamingSelectedItem() } } -void LLPanelInventory::draw() +void LLPanelObjectInventory::draw() { LLPanel::draw(); @@ -2089,14 +1926,14 @@ void LLPanelInventory::draw() } } -void LLPanelInventory::deleteAllChildren() +void LLPanelObjectInventory::deleteAllChildren() { mScroller = NULL; mFolders = NULL; LLView::deleteAllChildren(); } -BOOL LLPanelInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg) +BOOL LLPanelObjectInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg) { if (mFolders && mHaveInventory) { @@ -2126,9 +1963,9 @@ BOOL LLPanelInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDr } //static -void LLPanelInventory::idle(void* user_data) +void LLPanelObjectInventory::idle(void* user_data) { - LLPanelInventory* self = (LLPanelInventory*)user_data; + LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; if (self->mInventoryNeedsUpdate) @@ -2136,3 +1973,22 @@ void LLPanelInventory::idle(void* user_data) self->updateInventory(); } } + +void LLPanelObjectInventory::onFocusLost() +{ + // inventory no longer handles cut/copy/paste/delete + if (LLEditMenuHandler::gEditMenuHandler == mFolders) + { + LLEditMenuHandler::gEditMenuHandler = NULL; + } + + LLPanel::onFocusLost(); +} + +void LLPanelObjectInventory::onFocusReceived() +{ + // inventory now handles cut/copy/paste/delete + LLEditMenuHandler::gEditMenuHandler = mFolders; + + LLPanel::onFocusReceived(); +} diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h new file mode 100644 index 000000000..74a8ff7d3 --- /dev/null +++ b/indra/newview/llpanelobjectinventory.h @@ -0,0 +1,95 @@ +/** + * @file llpanelobjectinventory.h + * @brief LLPanelObjectInventory class definition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELOBJECTINVENTORY_H +#define LL_LLPANELOBJECTINVENTORY_H + +#include "llvoinventorylistener.h" +#include "llpanel.h" + +#include "llinventory.h" + +class LLScrollableContainerView; +class LLFolderView; +class LLFolderViewFolder; +class LLViewerObject; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLPanelObjectInventory +// +// This class represents the panel used to view and control a +// particular task's inventory. +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLPanelObjectInventory : public LLPanel, public LLVOInventoryListener +{ +public: + LLPanelObjectInventory(const std::string& name, const LLRect& rect); + virtual ~LLPanelObjectInventory(); + + void refresh(); + const LLUUID& getTaskUUID() { return mTaskUUID;} + void removeSelectedItem(); + void startRenamingSelectedItem(); + + LLFolderView* getRootFolder() const { return mFolders; } + + virtual void draw(); + virtual void deleteAllChildren(); + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg); + + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + static void idle(void* user_data); + +protected: + void reset(); + /*virtual*/ void inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data); + void updateInventory(); + void createFolderViews(LLInventoryObject* inventory_root, LLInventoryObject::object_list_t& contents); + void createViewsForCategory(LLInventoryObject::object_list_t* inventory, + LLInventoryObject* parent, + LLFolderViewFolder* folder); + + void clearContents(); + +private: + LLScrollableContainerView* mScroller; + LLFolderView* mFolders; + + LLUUID mTaskUUID; + BOOL mHaveInventory; + BOOL mIsInventoryEmpty; + BOOL mInventoryNeedsUpdate; +}; + +void init_object_inventory_panel_actions(LLPanelObjectInventory *panel); + +#endif // LL_LLPANELINVENTORY_H diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 812ec22a0..7bcb0eb31 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -160,16 +160,13 @@ BOOL LLPanelPick::postBuild() mLocationEditor = getChild("location_editor"); mSetBtn = getChild( "set_location_btn"); - mSetBtn->setClickedCallback(onClickSet); - mSetBtn->setCallbackUserData(this); + mSetBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickSet,this)); mTeleportBtn = getChild( "pick_teleport_btn"); - mTeleportBtn->setClickedCallback(onClickTeleport); - mTeleportBtn->setCallbackUserData(this); + mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickTeleport,this)); mMapBtn = getChild( "pick_map_btn"); - mMapBtn->setClickedCallback(onClickMap); - mMapBtn->setCallbackUserData(this); + mMapBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickMap,this)); mSortOrderText = getChild("sort_order_text"); diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp index 38b75572a..4f687099f 100644 --- a/indra/newview/llpanelplace.cpp +++ b/indra/newview/llpanelplace.cpp @@ -106,20 +106,16 @@ BOOL LLPanelPlace::postBuild() mLocationDisplay = getChild("location_editor"); mTeleportBtn = getChild( "teleport_btn"); - mTeleportBtn->setClickedCallback(onClickTeleport); - mTeleportBtn->setCallbackUserData(this); + mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPlace::onClickTeleport,this)); mMapBtn = getChild( "map_btn"); - mMapBtn->setClickedCallback(onClickMap); - mMapBtn->setCallbackUserData(this); + mMapBtn->setClickedCallback(boost::bind(&LLPanelPlace::onClickMap,this)); //mLandmarkBtn = getChild( "landmark_btn"); - //mLandmarkBtn->setClickedCallback(onClickLandmark); - //mLandmarkBtn->setCallbackUserData(this); + //mLandmarkBtn->setClickedCallback(boost::bind(&LLPanelPlace::onClickLandmark,this)); mAuctionBtn = getChild( "auction_btn"); - mAuctionBtn->setClickedCallback(onClickAuction); - mAuctionBtn->setCallbackUserData(this); + mAuctionBtn->setClickedCallback(boost::bind(&LLPanelPlace::onClickAuction,this)); // Default to no auction button. We'll show it if we get an auction id mAuctionBtn->setVisible(FALSE); diff --git a/indra/newview/llpanelskins.cpp b/indra/newview/llpanelskins.cpp index 828598c3d..3d5958a5f 100644 --- a/indra/newview/llpanelskins.cpp +++ b/indra/newview/llpanelskins.cpp @@ -126,9 +126,9 @@ void LLPanelSkins::refresh() "textures"+gDirUtilp->getDirDelimiter()+ imagename); b->setImages(imageprev,imageprev); - b->setHoverImages(imageprev,imageprev); - b->setScaleImage(TRUE); - + b->setImageHoverSelected(LLUI::getUIImage(imageprev)); + b->setImageHoverUnselected(LLUI::getUIImage(imageprev)); + //